Skip to Content
GuidesSetting Up Webhooks

Setting Up Webhooks

Webhooks allow your application to receive real-time HTTP notifications when events occur in Auris, such as a user signing up, a login failing, a role being assigned, or an organization being created. Instead of polling the API for changes, Auris pushes event payloads to your endpoint the moment something happens.

This guide walks you through creating a webhook receiver, registering it in the Auris Console, verifying signatures for security, and handling failures gracefully.

Why Webhooks

Without webhooks, your application would need to repeatedly poll Auris APIs to detect changes. This is inefficient, introduces latency (you only discover changes on your next poll cycle), and wastes resources on both sides. Webhooks invert this: Auris tells your application immediately when something happens.

Common use cases include:

  • Sync user data to your database when a user is created or updated
  • Trigger onboarding workflows when a new user signs up
  • Revoke access in your system when a user is disabled or deleted
  • Audit logging by streaming events to your SIEM
  • Slack/Teams notifications when suspicious login activity is detected

Step 1: Create a Webhook Endpoint in Your Application

Your webhook endpoint is a standard HTTP POST handler that receives JSON payloads from Auris. Here is a complete Express.js example with signature verification:

import express from 'express' import crypto from 'crypto' const app = express() // IMPORTANT: Use raw body for signature verification app.post( '/webhooks/auris', express.raw({ type: 'application/json' }), (req, res) => { const signature = req.headers['x-webhook-signature'] as string const timestamp = req.headers['x-webhook-timestamp'] as string const secret = process.env.AURIS_WEBHOOK_SECRET! // e.g. whsec_abc123... // 1. Verify the signature if (!verifyWebhookSignature(req.body, signature, timestamp, secret)) { console.error('Webhook signature verification failed') return res.status(401).json({ error: 'Invalid signature' }) } // 2. Parse the event const event = JSON.parse(req.body.toString()) console.log(`Received event: ${event.type}`, event.data) // 3. Respond 200 immediately — process asynchronously res.status(200).json({ received: true }) // 4. Handle the event asynchronously handleWebhookEvent(event).catch((err) => console.error('Webhook handler error:', err) ) } ) function verifyWebhookSignature( body: Buffer, signature: string, timestamp: string, secret: string ): boolean { // Reject if timestamp is older than 5 minutes (replay protection) const eventTime = parseInt(timestamp, 10) const now = Math.floor(Date.now() / 1000) if (Math.abs(now - eventTime) > 300) { return false } // Compute expected signature: HMAC-SHA256(timestamp.body) const payload = `${timestamp}.${body.toString()}` const expected = crypto .createHmac('sha256', secret) .update(payload) .digest('hex') // Constant-time comparison to prevent timing attacks try { return crypto.timingSafeEqual( Buffer.from(signature), Buffer.from(expected) ) } catch { return false } } async function handleWebhookEvent(event: { type: string data: Record<string, unknown> }) { switch (event.type) { case 'user.created': // Sync user to your local database await syncUser(event.data) break case 'user.deleted': // Remove user data from your system await removeUser(event.data) break case 'login.suspicious': // Alert your security team await notifySecurityTeam(event.data) break default: console.log(`Unhandled event type: ${event.type}`) } } app.listen(3000, () => console.log('Webhook server running on port 3000'))

Always use the raw request body (not the parsed JSON object) when computing the HMAC signature. Parsing and re-serializing JSON can change whitespace or key ordering, which invalidates the signature.

Step 2: Register the Webhook in Auris Console

  1. Open the Auris Console and navigate to Settings then Webhooks
  2. Click Create Webhook
  3. Enter your endpoint URL (e.g., https://api.yourapp.com/webhooks/auris)
  4. Select the events you want to receive (or choose “All events”)
  5. Click Create

Auris generates a signing secret prefixed with whsec_ (e.g., whsec_k7Gm2x9pQ...). Copy this value immediately and store it securely in your environment variables. The full secret is only displayed once.

You can also register webhooks via the API:

curl -X POST https://auth.yourdomain.com/api/webhooks \ -H "Authorization: Bearer $AURIS_ACCESS_TOKEN" \ -H "x-tenant: your-tenant-id" \ -H "Content-Type: application/json" \ -d '{ "name": "Production webhook", "url": "https://api.yourapp.com/webhooks/auris", "events": ["user.created", "user.updated", "user.deleted", "login.failed"], "isActive": true }'

Response:

{ "ok": true, "data": { "id": "whk_abc123", "name": "Production webhook", "url": "https://api.yourapp.com/webhooks/auris", "events": ["user.created", "user.updated", "user.deleted", "login.failed"], "signingSecret": "whsec_k7Gm2x9pQzR4vL8...", "isActive": true, "createdAt": "2026-01-15T10:00:00Z" } }

Step 3: Verify Webhook Signatures

Every webhook delivery from Auris includes two headers for verification:

HeaderDescription
X-Webhook-SignatureHMAC-SHA256 hex digest of the payload
X-Webhook-TimestampUnix timestamp (seconds) when the event was sent

The signature is computed as:

HMAC-SHA256(whsec_secret, "${timestamp}.${rawBody}")

Verification Algorithm

  1. Extract the X-Webhook-Signature and X-Webhook-Timestamp headers from the request
  2. Check the timestamp is within 5 minutes of the current time (replay protection)
  3. Concatenate the timestamp, a literal period (.), and the raw request body
  4. Compute the HMAC-SHA256 of that string using your whsec_ signing secret
  5. Compare your computed signature with the received signature using a constant-time comparison function

Here is a standalone verification function in TypeScript:

import crypto from 'crypto' export function verifyAurisWebhook( rawBody: string | Buffer, signature: string, timestamp: string, secret: string ): boolean { // Step 1: Replay protection — reject events older than 5 minutes const ts = parseInt(timestamp, 10) if (isNaN(ts) || Math.abs(Date.now() / 1000 - ts) > 300) { return false } // Step 2: Compute expected signature const body = typeof rawBody === 'string' ? rawBody : rawBody.toString('utf-8') const payload = `${timestamp}.${body}` const expected = crypto .createHmac('sha256', secret) .update(payload) .digest('hex') // Step 3: Constant-time comparison try { return crypto.timingSafeEqual( Buffer.from(signature, 'utf-8'), Buffer.from(expected, 'utf-8') ) } catch { return false } }

Using the Auris SDK for Verification

If you are using @auris/js, the SDK includes a built-in webhook verification utility:

import { verifyWebhookSignature } from '@auris/js/webhooks' const isValid = await verifyWebhookSignature({ payload: rawBody, signature: req.headers['x-webhook-signature'], timestamp: req.headers['x-webhook-timestamp'], secret: process.env.AURIS_WEBHOOK_SECRET!, })

Never skip signature verification in production. Without it, any attacker who discovers your endpoint URL can send forged events to your application.

Event Catalog

Auris sends events organized into the following categories. Each event payload includes a type field (e.g., user.created) and a data object containing the relevant resource.

User Events

EventTriggered when
user.createdA new user is registered (signup, admin creation, SCIM provisioning, or SSO JIT)
user.updatedUser profile fields are modified
user.deletedA user is soft-deleted
user.email_verifiedA user verifies their email address
user.password_changedA user changes their password
user.blockedA user account is disabled
user.unblockedA user account is re-enabled

Login Events

EventTriggered when
login.succeededA user successfully authenticates
login.failedA login attempt fails (wrong credentials, locked account, etc.)
login.mfa_requiredMFA step-up is triggered during login
login.suspiciousA login is flagged by suspicious login detection

Role and Permission Events

EventTriggered when
role.createdA new role is created
role.updatedA role’s name, description, or permissions are modified
role.deletedA role is deleted
role.assignedA role is assigned to a user
role.unassignedA role is removed from a user

Organization Events

EventTriggered when
organization.createdA new organization is created
organization.updatedOrganization details are modified
organization.deletedAn organization is deleted
organization.member_addedA user joins an organization
organization.member_removedA user is removed from an organization
organization.invitation_sentAn invitation is sent
organization.invitation_acceptedAn invitation is accepted

Application Events

EventTriggered when
application.createdA new application is registered
application.updatedApplication configuration is modified
application.secret_rotatedAn application’s client secret is rotated

Example Payload

{ "id": "evt_abc123def456", "type": "user.created", "timestamp": "2026-01-15T10:30:00Z", "data": { "id": "usr_xyz789", "email": "[email protected]", "firstName": "Jane", "lastName": "Doe", "emailVerified": false, "roles": [], "createdAt": "2026-01-15T10:30:00Z" } }

Handling Failures and Retries

If your endpoint returns a non-2xx status code or does not respond within 30 seconds, Auris considers the delivery failed and retries with exponential backoff:

AttemptDelay after failure
1st retry1 minute
2nd retry5 minutes
3rd retry30 minutes
4th retry2 hours
5th retry12 hours

After 5 failed retries (6 total attempts), the delivery is marked as permanently failed. You can view failed deliveries and manually retry them in the Console under Settings then Webhooks then select a webhook then Deliveries.

If a webhook endpoint consistently fails, Auris automatically disables the webhook after 10 consecutive failed deliveries and sends a notification to tenant administrators. Re-enable it from the Console after fixing the underlying issue.

Testing Webhooks

Console Test Button

In the Auris Console, each webhook has a Test button that sends a synthetic event to your endpoint. This is the fastest way to verify your endpoint is reachable and signature verification is working correctly.

Local Development with ngrok

During development, your local server is not publicly reachable. Use a tunneling tool like ngrok to expose it:

# Start your local webhook server node server.js # or: npx ts-node server.ts # In another terminal, start ngrok ngrok http 3000

ngrok provides a public URL like https://a1b2c3d4.ngrok-free.app. Use this URL when registering the webhook in the Console:

https://a1b2c3d4.ngrok-free.app/webhooks/auris

After registering, use the Console test button or trigger a real event (e.g., create a user) to see the payload arrive in your local server.

Inspecting Deliveries

The Console webhook detail page shows a delivery log with:

  • HTTP status code returned by your endpoint
  • Response body (first 1 KB)
  • Response time in milliseconds
  • Number of retry attempts
  • Timestamp of each delivery attempt

Best Practices

Respond 200 immediately. Your endpoint should return HTTP 200 as fast as possible, then process the event asynchronously (e.g., by queuing it). If your handler takes too long, Auris may time out and retry, leading to duplicate processing.

Implement idempotency. Every event includes a unique id field. Store processed event IDs and skip duplicates. Retries can deliver the same event multiple times.

async function handleWebhookEvent(event: { id: string; type: string; data: unknown }) { // Check if already processed const exists = await db.processedWebhookEvent.findUnique({ where: { eventId: event.id }, }) if (exists) { console.log(`Skipping duplicate event: ${event.id}`) return } // Process the event await processEvent(event) // Mark as processed await db.processedWebhookEvent.create({ data: { eventId: event.id, processedAt: new Date() }, }) }

Use HTTPS endpoints. Auris sends webhook payloads over HTTPS only. HTTP endpoints are rejected when registering the webhook.

Rotate secrets periodically. Use the Console or API to rotate your webhook signing secret. Auris supports secret rotation with a grace period where both old and new secrets are accepted.

curl -X POST https://auth.yourdomain.com/api/webhooks/whk_abc123/rotate-secret \ -H "Authorization: Bearer $AURIS_ACCESS_TOKEN" \ -H "x-tenant: your-tenant-id"

Monitor delivery health. Set up alerts on your endpoint error rate. If you see a spike in failed deliveries, check your server logs and the Console delivery log for details.

Filter events at registration. Only subscribe to the events your application needs. Subscribing to all events generates unnecessary traffic and processing load.

  • Log Streaming — Stream audit logs to external services like Datadog and Splunk
  • Actions Engine — Execute custom logic during authentication flows
  • Attack Protection — Security pipeline and suspicious login detection