# Real-time User Sync via Zitadel Webhooks (Actions v2)

**Available from release 2026.08**

Starting with release 2026.08, Unique supports real-time user synchronization via Zitadel Actions v2 webhooks. This replaces the 5-minute polling delay with near-instant event delivery (~1-3 seconds).

## How it works

When enabled, the scope-management service registers itself as a webhook target with Zitadel on startup. Zitadel then sends HTTP POST requests to the scope-management service whenever user or organization events occur (e.g., user created, user deleted, profile updated, role changed).

The following events are handled in real-time:

- Organization created, updated, or removed
- User created (including self-registration)
- User profile updated
- User removed
- Username changed
- User metadata set or removed (including group claims)
- User grant/role assignments changed

## Fallback mechanism

The existing scheduled user-sync job (every 5 minutes) is kept as an automatic fallback. If the webhook delivery fails (e.g., during a service restart), the polling job catches up on missed events during its next run. A deduplication mechanism ensures that events are never processed twice, even when both the webhook and the polling job handle the same event.

## Prerequisites

- Zitadel v3.4.5 or later (uses the `/v2beta/` Actions API)
- The `ZITADEL_WEBHOOK_ENDPOINT` environment variable must be set on the scope-management service to the URL reachable from Zitadel (e.g., `http://backend-service-scope-management:8094/webhook/zitadel-events`)
- `ZITADEL_GRPC_HOST` and `ZITADEL_PAT` must be configured (same as for the existing user-sync)

Note the `ZITADEL_PAT` must have `IamOwner` permissions to be able to create the actions webhook.

## Configuration

| Environment Variable      | Required            | Description                                                                                                                                                 |
|---------------------------|---------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `ZITADEL_WEBHOOK_ENDPOINT`| No (opt-in)         | URL that Zitadel will POST events to. Must be reachable from the Zitadel instance. When not set, only the polling sync is used.                             |
| `ZITADEL_GRPC_HOST`      | Yes (existing)      | Zitadel API URL                                                                                                                                          |
| `ZITADEL_PAT`            | Yes (existing)      | Personal Access Token for the Zitadel service user. <br>The service user must have the `Iam Owner` role to be able to create the actions.                  |

No additional setup is needed in Zitadel — the service automatically registers the webhook targets and event subscriptions on startup.

## Verification

On service startup, check the logs for:

- `Zitadel Actions v2 registration complete` — confirms the webhook target was registered
- `Reusing existing target` — on subsequent restarts, confirms the existing target is being reused
- `Handling webhook event` — confirms events are being received in real-time

If `ZITADEL_WEBHOOK_ENDPOINT` is not set, the log will show:

- `ZITADEL_WEBHOOK_ENDPOINT not configured — skipping Actions v2 registration`
