Webhooks

Webhooks

ℹ️
Available on Launch, Business, and Enterprise plans.

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.

Webhooks

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) ..."
  }
}
ℹ️
How enrichment works: Skycloak runs a Keycloak SPI (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:

  1. Concatenate the timestamp and raw request body: {timestamp}.{body}
  2. Compute HMAC-SHA256 using your webhook’s signing secret
  3. Compare with the X-Skycloak-Signature header
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

  1. Navigate to Webhooks in the sidebar
  2. Click Create Webhook
  3. Provide your HTTP endpoint URL
  4. Optionally provide an auth token (sent as Authorization: Bearer header)
  5. Select which event types to receive
  6. Optionally filter by specific cluster or realm
  7. Monitor delivery status directly on the webhook card

Limits

Plan Max Webhooks
Developer 0
Launch 2
Business 5
Enterprise Unlimited
Last updated on