Handle webhook events in your application
Receive real-time notifications about authentication events in your application using Scalekit webhooks
Webhooks provide real-time notifications about authentication and user management events in your Scalekit environment. Instead of polling for changes, your application receives instant notifications when users sign up, log in, join organizations, or when other important events occur.
Webhooks enable your app to react immediately to changes in your auth stack through:
- Real-time updates: Get notified immediately when events occur
- Reduced API calls: No need to poll for changes
- Event-driven architecture: Build responsive workflows that react to user actions
- Reliable delivery: Scalekit ensures webhook delivery with automatic retries
Webhook event object
Section titled “Webhook event object”All webhook payloads follow a standardized structure with metadata and event-specific data in the data field.
{ "spec_version": "1", // The version of the event specification format. Currently "1". "id": "evt_123456789", // A unique identifier for the event (e.g., evt_123456789). "object": "DirectoryUser", // The type of object that triggered the event (e.g., "DirectoryUser", "Directory", "Connection"). "environment_id": "env_123456789", // The ID of the environment where the event occurred. "occurred_at": "2024-08-21T10:20:17.072Z", // ISO 8601 timestamp indicating when the event occurred. "organization_id": "org_123456789", // The ID of the organization associated with the event. "type": "organization.directory.user_created", // The specific event type (e.g., "organization.directory.user_created"). "data": { // Event-specific payload containing details relevant to the event type. "user_id": "usr_123456789", "email": "user@example.com", "name": "John Doe" }}Configure webhooks in the dashboard
Section titled “Configure webhooks in the dashboard”Set up webhook endpoints and select which events you want to receive through the Scalekit dashboard.
-
In your Scalekit dashboard, navigate to Settings > Webhooks
-
Click Add Endpoint and provide:
- Endpoint URL - Your application’s webhook handler URL (e.g.,
https://yourapp.com/webhooks/scalekit) - Description - Optional description for this endpoint
- Endpoint URL - Your application’s webhook handler URL (e.g.,
-
Choose which events you want to receive from the dropdown:
- User events -
user.created,user.updated,user.deleted - Organization events -
organization.created,organization.updated - Authentication events -
session.created,session.expired - Membership events -
membership.created,membership.updated,membership.deleted
- User events -
-
Copy the Signing Secret - you’ll use this to verify webhook authenticity in your application
-
Use the Send Test Event button to verify your endpoint is working correctly
Implement webhook handlers
Section titled “Implement webhook handlers”Create secure webhook handlers in your application to process incoming events from Scalekit.
-
Set up webhook endpoint
Section titled “Set up webhook endpoint”Create an HTTP POST endpoint in your application to receive webhook payloads from Scalekit.
Express.js webhook handler 3 collapsed linesimport express from 'express';import { Scalekit } from '@scalekit-sdk/node';const app = express();const scalekit = new Scalekit(/* your credentials */);// Use raw body parser for webhook signature verificationapp.use('/webhooks/scalekit', express.raw({ type: 'application/json' }));app.post('/webhooks/scalekit', async (req, res) => {try {// Get webhook signature from headersconst signature = req.headers['scalekit-signature'];const rawBody = req.body;// Verify webhook signature using Scalekit SDKconst isValid = await scalekit.webhooks.verifySignature(rawBody,signature,process.env.SCALEKIT_WEBHOOK_SECRET);if (!isValid) {console.error('Invalid webhook signature');return res.status(401).json({ error: 'Invalid signature' });}// Parse and process the webhook payloadconst event = JSON.parse(rawBody.toString());await processWebhookEvent(event);// Always respond with 200 to acknowledge receiptres.status(200).json({ received: true });} catch (error) {console.error('Webhook processing error:', error);res.status(500).json({ error: 'Webhook processing failed' });}});Flask webhook handler 4 collapsed linesfrom flask import Flask, request, jsonifyimport jsonfrom scalekit import ScalekitClientapp = Flask(__name__)scalekit_client = ScalekitClient(/* your credentials */)@app.route('/webhooks/scalekit', methods=['POST'])def handle_webhook():try:# Get webhook signature from headerssignature = request.headers.get('scalekit-signature')raw_body = request.get_data()# Verify webhook signature using Scalekit SDKis_valid = scalekit_client.webhooks.verify_signature(raw_body,signature,os.environ.get('SCALEKIT_WEBHOOK_SECRET'))if not is_valid:print('Invalid webhook signature')return jsonify({'error': 'Invalid signature'}), 401# Parse and process the webhook payloadevent = json.loads(raw_body.decode('utf-8'))process_webhook_event(event)# Always respond with 200 to acknowledge receiptreturn jsonify({'received': True}), 200except Exception as error:print(f'Webhook processing error: {error}')return jsonify({'error': 'Webhook processing failed'}), 500Gin webhook handler 8 collapsed linespackage mainimport ("encoding/json""io""net/http""github.com/gin-gonic/gin""github.com/scalekit-inc/scalekit-sdk-go")scalekitClient := scalekit.NewScalekitClient(/* your credentials */)func handleWebhook(c *gin.Context) {// Get webhook signature from headerssignature := c.GetHeader("scalekit-signature")// Read raw bodyrawBody, err := io.ReadAll(c.Request.Body)if err != nil {c.JSON(http.StatusBadRequest, gin.H{"error": "Failed to read body"})return}// Verify webhook signature using Scalekit SDKisValid, err := scalekitClient.Webhooks.VerifySignature(rawBody,signature,os.Getenv("SCALEKIT_WEBHOOK_SECRET"),)if err != nil || !isValid {c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid signature"})return}// Parse and process the webhook payloadvar event map[string]interface{}if err := json.Unmarshal(rawBody, &event); err != nil {c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid JSON"})return}processWebhookEvent(event)// Always respond with 200 to acknowledge receiptc.JSON(http.StatusOK, gin.H{"received": true})}Spring webhook handler 8 collapsed linesimport org.springframework.web.bind.annotation.*;import org.springframework.http.ResponseEntity;import org.springframework.http.HttpStatus;import com.scalekit.ScalekitClient;import com.fasterxml.jackson.databind.ObjectMapper;import javax.servlet.http.HttpServletRequest;import java.io.IOException;@RestControllerpublic class WebhookController {private final ScalekitClient scalekitClient;private final ObjectMapper objectMapper = new ObjectMapper();@PostMapping("/webhooks/scalekit")public ResponseEntity<Map<String, Object>> handleWebhook(HttpServletRequest request,@RequestBody String rawBody) {try {// Get webhook signature from headersString signature = request.getHeader("scalekit-signature");// Verify webhook signature using Scalekit SDKboolean isValid = scalekitClient.webhooks().verifySignature(rawBody.getBytes(),signature,System.getenv("SCALEKIT_WEBHOOK_SECRET"));if (!isValid) {return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Map.of("error", "Invalid signature"));}// Parse and process the webhook payloadMap<String, Object> event = objectMapper.readValue(rawBody, Map.class);processWebhookEvent(event);// Always respond with 200 to acknowledge receiptreturn ResponseEntity.ok(Map.of("received", true));} catch (Exception error) {return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Map.of("error", "Webhook processing failed"));}}} -
Process webhook events
Section titled “Process webhook events”Handle different event types based on your application’s needs.
Process webhook events async function processWebhookEvent(event) {console.log(`Processing event: ${event.type}`);switch (event.type) {case 'user.created':// Handle new user registrationawait handleUserCreated(event.data.user, event.data.organization);break;case 'user.updated':// Handle user profile updatesawait handleUserUpdated(event.data.user);break;case 'organization.created':// Handle new organization creationawait handleOrganizationCreated(event.data.organization);break;case 'membership.created':// Handle user joining organizationawait handleMembershipCreated(event.data.membership);break;default:console.log(`Unhandled event type: ${event.type}`);}}async function handleUserCreated(user, organization) {// Use case: Sync new user to your database, send welcome email, set up user workspaceconsole.log(`New user created: ${user.email} in org: ${organization.display_name}`);// Sync to your databaseawait syncUserToDatabase(user, organization);// Send welcome emailawait sendWelcomeEmail(user.email, user.first_name);// Set up user workspace or default settingsawait setupUserDefaults(user.id, organization.id);}Process webhook events def process_webhook_event(event):print(f'Processing event: {event["type"]}')event_type = event['type']event_data = event['data']if event_type == 'user.created':# Handle new user registrationhandle_user_created(event_data['user'], event_data['organization'])elif event_type == 'user.updated':# Handle user profile updateshandle_user_updated(event_data['user'])elif event_type == 'organization.created':# Handle new organization creationhandle_organization_created(event_data['organization'])elif event_type == 'membership.created':# Handle user joining organizationhandle_membership_created(event_data['membership'])else:print(f'Unhandled event type: {event_type}')def handle_user_created(user, organization):# Use case: Sync new user to your database, send welcome email, set up user workspaceprint(f'New user created: {user["email"]} in org: {organization["display_name"]}')# Sync to your databasesync_user_to_database(user, organization)# Send welcome emailsend_welcome_email(user['email'], user['first_name'])# Set up user workspace or default settingssetup_user_defaults(user['id'], organization['id'])Process webhook events func processWebhookEvent(event map[string]interface{}) {eventType := event["type"].(string)eventData := event["data"].(map[string]interface{})fmt.Printf("Processing event: %s\n", eventType)switch eventType {case "user.created":// Handle new user registrationuser := eventData["user"].(map[string]interface{})organization := eventData["organization"].(map[string]interface{})handleUserCreated(user, organization)case "user.updated":// Handle user profile updatesuser := eventData["user"].(map[string]interface{})handleUserUpdated(user)case "organization.created":// Handle new organization creationorganization := eventData["organization"].(map[string]interface{})handleOrganizationCreated(organization)case "membership.created":// Handle user joining organizationmembership := eventData["membership"].(map[string]interface{})handleMembershipCreated(membership)default:fmt.Printf("Unhandled event type: %s\n", eventType)}}func handleUserCreated(user, organization map[string]interface{}) {// Use case: Sync new user to your database, send welcome email, set up user workspacefmt.Printf("New user created: %s in org: %s\n",user["email"], organization["display_name"])// Sync to your databasesyncUserToDatabase(user, organization)// Send welcome emailsendWelcomeEmail(user["email"].(string), user["first_name"].(string))// Set up user workspace or default settingssetupUserDefaults(user["id"].(string), organization["id"].(string))}Process webhook events private void processWebhookEvent(Map<String, Object> event) {String eventType = (String) event.get("type");Map<String, Object> eventData = (Map<String, Object>) event.get("data");System.out.println("Processing event: " + eventType);switch (eventType) {case "user.created":// Handle new user registrationMap<String, Object> user = (Map<String, Object>) eventData.get("user");Map<String, Object> organization = (Map<String, Object>) eventData.get("organization");handleUserCreated(user, organization);break;case "user.updated":// Handle user profile updateshandleUserUpdated((Map<String, Object>) eventData.get("user"));break;case "organization.created":// Handle new organization creationhandleOrganizationCreated((Map<String, Object>) eventData.get("organization"));break;case "membership.created":// Handle user joining organizationhandleMembershipCreated((Map<String, Object>) eventData.get("membership"));break;default:System.out.println("Unhandled event type: " + eventType);}}private void handleUserCreated(Map<String, Object> user, Map<String, Object> organization) {// Use case: Sync new user to your database, send welcome email, set up user workspaceSystem.out.println("New user created: " + user.get("email") +" in org: " + organization.get("display_name"));// Sync to your databasesyncUserToDatabase(user, organization);// Send welcome emailsendWelcomeEmail((String) user.get("email"), (String) user.get("first_name"));// Set up user workspace or default settingssetupUserDefaults((String) user.get("id"), (String) organization.get("id"));} -
Verify webhook signature
Section titled “Verify webhook signature”Use the Scalekit SDK to verify webhook signatures before processing events.
Signature verification async function verifyWebhookSignature(rawBody, signature, secret) {try {// Use Scalekit SDK for verification (recommended)const isValid = await scalekit.webhooks.verifySignature(rawBody, signature, secret);return isValid;} catch (error) {console.error('Signature verification failed:', error);return false;}}Signature verification def verify_webhook_signature(raw_body, signature, secret):try:# Use Scalekit SDK for verification (recommended)is_valid = scalekit_client.webhooks.verify_signature(raw_body, signature, secret)return is_validexcept Exception as error:print(f'Signature verification failed: {error}')return FalseSignature verification func verifyWebhookSignature(rawBody []byte, signature string, secret string) (bool, error) {// Use Scalekit SDK for verification (recommended)isValid, err := scalekitClient.Webhooks.VerifySignature(rawBody, signature, secret)if err != nil {fmt.Printf("Signature verification failed: %v\n", err)return false, err}return isValid, nil}Signature verification private boolean verifyWebhookSignature(byte[] rawBody, String signature, String secret) {try {// Use Scalekit SDK for verification (recommended)boolean isValid = scalekitClient.webhooks().verifySignature(rawBody, signature, secret);return isValid;} catch (Exception error) {System.err.println("Signature verification failed: " + error.getMessage());return false;}}
Respond to webhook event
Section titled “Respond to webhook event”Scalekit expects specific HTTP status codes in response to webhook deliveries. Return appropriate status codes to control retry behavior.
-
Return success responses
Section titled “Return success responses”Return success status codes when webhooks are processed successfully.
Status Code Description 200 OKWebhook processed successfully 201 CreatedRecommendedWebhook processed and resource created 202 AcceptedWebhook accepted for asynchronous processing -
Handle error responses
Section titled “Handle error responses”Return error status codes to indicate processing failures.
Status Code Description 400 Bad RequestInvalid payload or malformed request 401 UnauthorizedInvalid webhook signature 403 ForbiddenWebhook not authorized 422 Unprocessable EntityValid request but cannot process 500 Internal Server ErrorServer error during processing
Testing webhooks
Section titled “Testing webhooks”Test your webhook implementation locally before deploying to production.
Use ngrok to expose your local development server for webhook testing.
# Install ngroknpm install -g ngrok
# Start your local servernpm run dev
# In another terminal, expose your local serverngrok http 3000
# Use the ngrok URL in your Scalekit dashboard# Example: https://abc123.ngrok.io/webhooks/scalekitCommon webhook use cases
Section titled “Common webhook use cases”Webhooks enable common integration patterns:
- User lifecycle management: Sync user data across systems, provision accounts in downstream services, and trigger onboarding workflows when users sign up or update their profiles
- Organization and membership management: Set up workspaces when organizations are created, update user access when they join or leave organizations, and provision organization-specific resources
- Authentication monitoring: Track login patterns, update last-seen timestamps, and trigger security alerts for suspicious activity
Webhook event reference
Section titled “Webhook event reference”You now have a complete webhook implementation that can reliably process authentication events from Scalekit. Consider these additional improvements: