Webhooks
Receive real-time Keycloak events via HTTP POST to your configured endpoints. Webhooks let you integrate authentication events into your own monitoring, analytics, security, or workflow automation systems.

Key Features
- Near real-time delivery: Events are delivered within 3-5 seconds via HTTP POST
- Enriched data: Automatic enrichment with IP geolocation and user-agent parsing
- HMAC-SHA256 signing: Every delivery is signed with a per-webhook secret for payload authenticity
- Flexible filtering: Filter by event type, realm, or cluster
- Inline delivery status: Visual green/red dots show recent delivery health at a glance
- Auto-retry: Failed deliveries are retried with exponential backoff
- Multi-region: Webhooks work across all Skycloak regions (US, CA, EU, AU)
Event Enrichment
Webhook payloads are enriched with additional context that Keycloak does not natively provide:
IP Geolocation
Every event with a client IP address is enriched with geographic data using MaxMind GeoLite2:
{
"geo": {
"country": "Canada",
"country_code": "CA",
"city": "Toronto",
"location": { "lat": 43.709, "lon": -79.406 }
}
}User-Agent
Browser, operating system, and device information is parsed from the HTTP User-Agent header:
{
"user_agent": {
"browser": "Chrome",
"browser_version": "125.0.0.0",
"os": "macOS",
"os_version": "10.15.7",
"device": "Macintosh",
"device_type": "desktop",
"raw": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) ..."
}
}keycloak-audit-spi) baked into every Keycloak base image. The SPI’s JAX-RS request filter captures the User-Agent header in-process on every event — no log correlation, no timing tolerance, no missed lookups. The raw header is then parsed into structured browser/OS/device fields and attached to the webhook payload.Webhook Payload
Each webhook delivery sends a JSON payload with the following structure:
{
"@timestamp": "2026-04-08T15:30:00.000Z",
"type": "LOGIN",
"cluster_id": "9aee835b-79f7-4c40-9aa9-c1d77f885776",
"workspace_id": "22fc21b0-a15b-4eac-ad9f-d4967f4c59d1",
"realm_id": "887c7d26-261c-4877-9327-6e96ed81120d",
"realm_name": "production",
"client_id": "my-app",
"user_id": "fe8c0c0b-f1af-4d04-a866-0551f4311308",
"username": "[email protected]",
"ip_address": "203.0.113.42",
"geo": {
"country": "Canada",
"country_code": "CA",
"city": "Toronto",
"location": { "lat": 43.709, "lon": -79.406 }
},
"user_agent": {
"browser": "Chrome",
"browser_version": "125.0.0.0",
"os": "macOS",
"device_type": "desktop"
}
}Admin Events
Admin operation events include additional fields and a field-level diff:
{
"type": "ADMIN_EVENT",
"operation_type": "UPDATE",
"resource_type": "CLIENT",
"resource_path": "clients/abc-123",
"representation": "{\"id\":\"abc-123\",\"name\":\"new name\",...}",
"diff_before": "{\"name\":\"old name\",\"directAccessGrantsEnabled\":false}",
"diff_after": "{\"name\":\"new name\",\"directAccessGrantsEnabled\":true}",
"changed_fields": ["name", "directAccessGrantsEnabled"]
}| Field | Operations | What it carries |
|---|---|---|
representation |
CREATE, UPDATE | Full post-state of the resource as a JSON string. Mirrors diff_after for newly-released code; kept under this name for legacy webhook consumers. |
diff_before |
UPDATE, DELETE | Pre-state JSON. null on CREATE — the resource didn’t exist. |
diff_after |
CREATE, UPDATE | Post-state JSON. null on DELETE — the resource is gone. |
changed_fields |
CREATE, UPDATE, DELETE | Dotted paths of keys whose value actually changed. Only fields the operator actually touched — Keycloak’s normalisation defaults that just appear in the post-state (e.g. alwaysDisplayInConsole=false, empty-string attributes) are filtered out. |
The diff is captured by the audit-SPI’s pre-fetch + post-capture pipeline: the SPI snapshots the resource just before Keycloak applies the update, then diffs against the post-state. This gives you a defensible audit trail showing exactly what changed (and from what value to what value), suitable for SOC 2 / change-management evidence.
Webhook Security
Each webhook is assigned a unique HMAC-SHA256 signing secret. Every delivery includes these headers:
| Header | Description |
|---|---|
X-Skycloak-Signature |
HMAC-SHA256 signature of timestamp.payload
|
X-Skycloak-Timestamp |
Unix timestamp of the delivery |
X-Skycloak-Delivery-ID |
Unique delivery identifier |
Verifying Signatures
To verify a webhook delivery:
- Concatenate the timestamp and raw request body:
{timestamp}.{body} - Compute HMAC-SHA256 using your webhook’s signing secret
- Compare with the
X-Skycloak-Signatureheader
import hmac
import hashlib
def verify_webhook(body: bytes, timestamp: str, signature: str, secret: str) -> bool:
message = f"{timestamp}.{body.decode()}"
expected = hmac.new(secret.encode(), message.encode(), hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature)Supported Event Types
Authentication Events
-
LOGIN/LOGIN_ERROR— User login via browser -
LOGOUT/LOGOUT_ERROR— User logout -
REGISTER/REGISTER_ERROR— New user registration -
CLIENT_LOGIN/CLIENT_LOGIN_ERROR— Service account login -
CODE_TO_TOKEN/CODE_TO_TOKEN_ERROR— Authorization code exchange -
REFRESH_TOKEN/REFRESH_TOKEN_ERROR— Token refresh
User Profile Events
-
UPDATE_EMAIL— Email change -
UPDATE_PASSWORD— Password change -
UPDATE_PROFILE— Profile update -
VERIFY_EMAIL— Email verification -
RESET_PASSWORD— Password reset
MFA Events
-
UPDATE_TOTP/REMOVE_TOTP— TOTP configuration changes
Admin Events
-
CREATE/UPDATE/DELETE/ACTION— Administrative operations on resources
Delivery & Retry
- Deliveries that receive a 2xx response are marked as successful
- Failed deliveries are retried with exponential backoff
- After 10 consecutive failures, the webhook is automatically disabled
- Delivery history is visible directly on the webhook card (green/red dots)
- Click the delivery indicator or the history button for full delivery details
Getting Started
- Navigate to Webhooks in the sidebar
- Click Create Webhook
- Provide your HTTP endpoint URL
- Optionally provide an auth token (sent as
Authorization: Bearerheader) - Select which event types to receive
- Optionally filter by specific cluster or realm
- Monitor delivery status directly on the webhook card
Limits
| Plan | Max Webhooks |
|---|---|
| Developer | 0 |
| Launch | 2 |
| Business | 5 |
| Enterprise | Unlimited |