Overview
The ActionServiceApi provides methods for managing action targets in Zitadel. Targets are webhook endpoints that can be triggered during various events in the authentication and authorization flow.
Initialize the API
require 'zitadel/client'
client = Zitadel :: Client :: ApiClient . new
client. config . access_token = 'YOUR_ACCESS_TOKEN'
action_api = Zitadel :: Client :: Api :: ActionServiceApi . new (client)
Target Management
Create a new target to your endpoint, which can be used in executions. Required permission: action.target.writerequest = Zitadel :: Client :: ActionServiceCreateTargetRequest . new (
name: 'User Created Webhook' ,
endpoint: 'https://api.myapp.com/webhooks/user-created' ,
target_type: 'TARGET_TYPE_WEBHOOK' ,
timeout: '10s' ,
async: false ,
payload_type: 'PAYLOAD_TYPE_JSON'
)
response = action_api. create_target (request)
puts "Created target: #{ response. id } "
The ID of the created target
Delete an existing target. This will remove it from any configured execution as well. Required permission: action.target.deleterequest = Zitadel :: Client :: ActionServiceDeleteTargetRequest . new (
target_id: 'target_123'
)
response = action_api. delete_target (request)
Public Key Management (JWE Encryption)
Adds a public key to the target for payload encryption. The key is used when payload type is set to PAYLOAD_TYPE_JWE. Required permission: action.target.write# Read your public key from file (PEM format)
public_key_pem = File . read ( '/path/to/public_key.pem' )
request = Zitadel :: Client :: ActionServiceAddPublicKeyRequest . new (
target_id: 'target_123' ,
public_key: public_key_pem,
expiration_date: Time . now + 365 * 24 * 3600 # 1 year
)
response = action_api. add_public_key (request)
puts "Key ID: #{ response. key_id } "
Public key in PEM format (RSA or EC)
Optional expiration date for the key
Activates the public key for payload encryption. Only one key can be active at a time. Required permission: action.target.writerequest = Zitadel :: Client :: ActionServiceActivatePublicKeyRequest . new (
target_id: 'target_123' ,
key_id: 'key_456'
)
response = action_api. activate_public_key (request)
Deactivates the public key for payload encryption. Use in break-glass scenarios to quickly disable a compromised key. Required permission: action.target.writerequest = Zitadel :: Client :: ActionServiceDeactivatePublicKeyRequest . new (
target_id: 'target_123' ,
key_id: 'key_456'
)
response = action_api. deactivate_public_key (request)
Target Types
Webhook Target
Standard HTTP webhook endpoint:
target_type: 'TARGET_TYPE_WEBHOOK'
endpoint: 'https://api.myapp.com/webhook'
Request Target
Custom HTTP request with configurable method:
target_type: 'TARGET_TYPE_REQUEST'
endpoint: 'https://api.myapp.com/action'
Async Target
Asynchronous execution without waiting for response:
Payload Types
JSON Payload
Standard JSON payload:
payload_type: 'PAYLOAD_TYPE_JSON'
JWE Encrypted Payload
Encrypted payload using JWE (JSON Web Encryption):
payload_type: 'PAYLOAD_TYPE_JWE'
When using JWE, you must:
Add a public key to the target
Activate the key
The payload will be encrypted with the active key
The kid header in the JWE token indicates which key was used
Example: Create Webhook with Encryption
# Step 1: Create the target
create_request = Zitadel :: Client :: ActionServiceCreateTargetRequest . new (
name: 'Secure User Webhook' ,
endpoint: 'https://api.myapp.com/webhooks/secure' ,
target_type: 'TARGET_TYPE_WEBHOOK' ,
timeout: '30s' ,
async: false ,
payload_type: 'PAYLOAD_TYPE_JWE'
)
target_response = action_api. create_target (create_request)
target_id = target_response. id
puts "Created target: #{ target_id } "
# Step 2: Add public key for encryption
public_key = File . read ( '/path/to/rsa_public_key.pem' )
key_request = Zitadel :: Client :: ActionServiceAddPublicKeyRequest . new (
target_id: target_id,
public_key: public_key,
expiration_date: Time . now + 365 * 24 * 3600
)
key_response = action_api. add_public_key (key_request)
key_id = key_response. key_id
puts "Added key: #{ key_id } "
# Step 3: Activate the key
activate_request = Zitadel :: Client :: ActionServiceActivatePublicKeyRequest . new (
target_id: target_id,
key_id: key_id
)
action_api. activate_public_key (activate_request)
puts "Key activated. Target is ready for encrypted payloads."
Example: Key Rotation
# Add new key
new_key = File . read ( '/path/to/new_public_key.pem' )
add_request = Zitadel :: Client :: ActionServiceAddPublicKeyRequest . new (
target_id: 'target_123' ,
public_key: new_key,
expiration_date: Time . now + 365 * 24 * 3600
)
new_key_response = action_api. add_public_key (add_request)
new_key_id = new_key_response. key_id
puts "New key added: #{ new_key_id } "
# Activate new key (this deactivates the old one)
activate_request = Zitadel :: Client :: ActionServiceActivatePublicKeyRequest . new (
target_id: 'target_123' ,
key_id: new_key_id
)
action_api. activate_public_key (activate_request)
puts "New key activated. Old key is now inactive."
Example: Emergency Key Deactivation
# In case of a compromised key, quickly deactivate it
deactivate_request = Zitadel :: Client :: ActionServiceDeactivatePublicKeyRequest . new (
target_id: 'target_123' ,
key_id: 'compromised_key_id'
)
action_api. deactivate_public_key (deactivate_request)
puts "Compromised key deactivated."
puts "Warning: Target will fail for JWE payloads until a new key is activated."
Webhook Payload Structure
When a target is triggered, it receives a payload with information about the event:
JSON Payload Example
{
"event" : "user.created" ,
"timestamp" : "2024-01-15T10:30:00Z" ,
"data" : {
"userId" : "user_123" ,
"email" : "[email protected] " ,
"organizationId" : "org_456"
}
}
JWE Payload
Encrypted payloads use JWE compact serialization:
eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIiwia2lkIjoia2V5XzEyMyJ9...
The payload includes:
kid header: Indicates which public key was used for encryption
alg header: Algorithm used (RSA-OAEP-256 or ECDH-ES+A256KW)
enc header: Content encryption method (A256GCM)
Decrypting JWE Payloads
require 'json/jwt'
def decrypt_jwe_payload ( jwe_token , private_key_pem )
private_key = OpenSSL :: PKey :: RSA . new (private_key_pem)
jwt = JSON :: JWT . decode (jwe_token, private_key)
jwt. to_h
end
# In your webhook handler
private_key = File . read ( '/path/to/private_key.pem' )
encrypted_payload = request. body . read
decrypted_data = decrypt_jwe_payload (encrypted_payload, private_key)
puts "Event: #{ decrypted_data[ 'event' ] } "
puts "Data: #{ decrypted_data[ 'data' ]. inspect } "
Target Configuration Options
Display name for the target
URL endpoint for the webhook
Type of target: TARGET_TYPE_WEBHOOK or TARGET_TYPE_REQUEST
Request timeout (e.g., ”10s”, ”30s”)
Whether to execute asynchronously
Payload format: PAYLOAD_TYPE_JSON or PAYLOAD_TYPE_JWE
Whether to interrupt the flow on target failure
Best Practices
Use JWE for Sensitive Data : Always encrypt payloads containing PII or sensitive information
Implement Retry Logic : Your webhook endpoint should handle retries gracefully
Validate Signatures : Verify that requests are actually from Zitadel
Monitor Timeouts : Set appropriate timeouts based on your endpoint’s response time
Key Rotation : Regularly rotate encryption keys and update them before expiration
Test Async Behavior : Async targets don’t block the auth flow, test accordingly
Handle Errors : Implement proper error handling and logging in your webhook
Common Use Cases
User Provisioning : Trigger user creation in external systems
Audit Logging : Send authentication events to SIEM systems
Data Synchronization : Keep user data in sync across systems
Notification : Send alerts on security events
Compliance : Record authentication events for compliance
See Also