Zitadel setup walkthrough
How to wire Targets and Executions so ZITADEL reaches the right Atrium Action endpoints.
This page is about the webhook plumbing, not script authoring.
Use it when you need to:
- create Targets
- rotate signing keys
- wire Executions
- understand which endpoint serves which condition
The mental model
ZITADEL Actions v2 has three moving parts:
- Endpoint — the HTTP URL you host
- Target — ZITADEL's saved definition of that endpoint
- Execution — the condition that calls one or more Targets
Atrium owns:
- the script source
- the runtime endpoints
- the admin APIs that help create Targets/Executions
ZITADEL owns:
- the Targets
- the Executions
- the decision of when to call them
The canonical Atrium endpoints
Atrium exposes one endpoint per transport family:
| Target name | Endpoint | Purpose |
|---|---|---|
atrium-actions-events | https://atrium.ommax-intelligence.de/actions/events | all Event deliveries |
atrium-actions-functions-preuserinfo | https://atrium.ommax-intelligence.de/actions/functions/preuserinfo | FUNCTION:preuserinfo |
atrium-actions-functions-preaccesstoken | https://atrium.ommax-intelligence.de/actions/functions/preaccesstoken | FUNCTION:preaccesstoken |
atrium-actions-functions-presamlresponse | https://atrium.ommax-intelligence.de/actions/functions/presamlresponse | FUNCTION:presamlresponse |
atrium-actions-requests | https://atrium.ommax-intelligence.de/actions/requests | Request executions |
atrium-actions-responses | https://atrium.ommax-intelligence.de/actions/responses | Response executions |
For each Target:
- type: REST webhook
- payload: JSON
- timeout:
5s - interrupt on error: usually unchecked unless you intentionally want synchronous blocking behaviour
Event-side vs script-side routing
Two different routing layers matter:
ZITADEL Execution routing
Event Executions can be scoped to:
allgroupevent
Atrium script routing
Once the request reaches Atrium, the runtime matches by exact slot:
EVENT:user.human.addedEVENT:oidc_session.addedFUNCTION:preuserinfo
So an all events Execution can still be valid, but the script itself still
needs the exact trigger name.
Recommended setup path
Path A: Atrium admin API
Use this when you already have Atrium admin auth.
Create a Target:
curl -X POST \
-H "Authorization: Bearer atr_live_..." \
-H "Content-Type: application/json" \
https://atrium.ommax-intelligence.de/api/admin/zitadel/targets \
-d '{
"name": "atrium-actions-events",
"endpoint": "https://atrium.ommax-intelligence.de/actions/events",
"payloadType": "PAYLOAD_TYPE_JSON",
"timeout": "5s",
"interruptOnError": false
}'
Wire an Execution:
curl -X POST \
-H "Authorization: Bearer atr_live_..." \
-H "Content-Type: application/json" \
https://atrium.ommax-intelligence.de/api/admin/zitadel/executions \
-d '{
"condition": { "event": { "all": true } },
"targetIds": ["<events-target-id>"]
}'
Path B: Zitadel Console
Use this if you must work directly in ZITADEL.
The tricky parts:
- the Console is worse than the API for signing-key recovery
- the important routing concept is the Execution condition, not just the Target
Signing keys
Each Target has its own signing key.
The runtime accepts:
ZITADEL_WEBHOOK_SIGNING_KEY- or the preferred CSV form
ZITADEL_WEBHOOK_SIGNING_KEYS
Operational rule:
- keep all active Target keys present on the runtime
- during rotation, keep old + new until traffic is clean
The two OMMAX Function slots that matter most
For the current migrations, the important Function slots are:
preuserinfo
Use for:
- userinfo / ID-token-adjacent claim mutation
flatRolesparity when consumers read these claims there
preaccesstoken
Use for:
- access-token claim mutation
- the same
flatRoleslogic when downstream apps read the access token
Often the correct OMMAX setup is both slots, not just one.
Current trigger advice
Choose these on purpose:
EVENT:user.human.addedfor first-time onboardingEVENT:oidc_session.addedfor login-time catch-upFUNCTION:preuserinfo/FUNCTION:preaccesstokenfor claims
Avoid guessing from names like session.added. Prefer recorded observed traffic.
Disable temporarily
If you want to stop a ZITADEL routing path without deleting the Target, remove its Execution by replacing the target list with an empty array.