Skip to main content

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.write
request = 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}"
id
string
The ID of the created target
Delete an existing target. This will remove it from any configured execution as well.Required permission: action.target.delete
request = 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}"
target_id
string
required
The ID of the target
public_key
string
required
Public key in PEM format (RSA or EC)
expiration_date
timestamp
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.write
request = 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.write
request = 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:
async: true

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:
  1. Add a public key to the target
  2. Activate the key
  3. The payload will be encrypted with the active key
  4. 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

name
string
required
Display name for the target
endpoint
string
required
URL endpoint for the webhook
target_type
enum
required
Type of target: TARGET_TYPE_WEBHOOK or TARGET_TYPE_REQUEST
timeout
duration
Request timeout (e.g., ”10s”, ”30s”)
async
boolean
Whether to execute asynchronously
payload_type
enum
Payload format: PAYLOAD_TYPE_JSON or PAYLOAD_TYPE_JWE
interrupt_on_error
boolean
Whether to interrupt the flow on target failure

Best Practices

  1. Use JWE for Sensitive Data: Always encrypt payloads containing PII or sensitive information
  2. Implement Retry Logic: Your webhook endpoint should handle retries gracefully
  3. Validate Signatures: Verify that requests are actually from Zitadel
  4. Monitor Timeouts: Set appropriate timeouts based on your endpoint’s response time
  5. Key Rotation: Regularly rotate encryption keys and update them before expiration
  6. Test Async Behavior: Async targets don’t block the auth flow, test accordingly
  7. 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

Build docs developers (and LLMs) love