Workflow Steps
Workflow steps are the actions a workflow runs when it triggers. Each step has a type and a config block. Steps run in order unless a condition step redirects flow.
Step Types
Action Steps
Action steps perform operations such as sending commands, logging messages, or making HTTP requests.
RCON Command (rcon_command)
Executes an RCON command on the server.
Configuration:
command(required) - The RCON command to execute
Example:
{
"name": "Broadcast Warning",
"type": "action",
"config": {
"action_type": "rcon_command",
"command": "AdminBroadcast Player ${trigger_event.player_name} has been warned for teamkilling"
}
}Common RCON Commands:
AdminBroadcast <message>- Broadcast message to all playersAdminWarn <player_id> <message>- Send warning to specific player (accepts Steam ID or EOS ID)AdminKick <player_id> <reason>- Kick player from server (accepts Steam ID or EOS ID)AdminBan <player_id> <duration> <reason>- Ban player (accepts Steam ID or EOS ID, duration in days:1d= 1 day,1M= 1 month,0= permanent)AdminForceTeamChange <player_id>- Force player to switch teams (accepts Steam ID or EOS ID)AdminChangeMap <layer>- Change the current map layerAdminSetMaxNumPlayers <number>- Set maximum player countAdminSlowMo <multiplier>- Change game speed (admin testing)AdminAlwaysValidPlacement <0|1>- Toggle placement validationAdminDisbandSquad <team_id> <squad_id>- Disband a squad
Admin Broadcast (admin_broadcast)
Sends a broadcast message visible to all players.
Configuration:
message(required) - The message to broadcast
Example:
{
"name": "Welcome New Player",
"type": "action",
"config": {
"action_type": "admin_broadcast",
"message": "Welcome ${trigger_event.player_name} to our server! Please read the rules."
}
}Chat Message (chat_message)
Sends a private chat message to a specific player.
Configuration:
target_player(required) - Player ID (Steam ID or player name) to send message tomessage(required) - The chat message to send
Example:
{
"name": "Auto Reply to Help",
"type": "action",
"config": {
"action_type": "chat_message",
"target_player": "${trigger_event.steam_id}",
"message": "Hi ${trigger_event.player_name}! Type !rules for server rules."
}
}Kick Player (kick_player)
Kicks a player from the server.
Configuration:
player_id(required) - Player ID (Steam ID, EOS ID, or player name) to kickreason(optional) - Reason for the kick
Example:
{
"name": "Kick Teamkiller",
"type": "action",
"config": {
"action_type": "kick_player",
"player_id": "${trigger_event.steam_id}",
"reason": "Repeated teamkilling"
}
}Ban Player (ban_player)
Bans a player from the server.
Configuration:
player_id(required) - Player ID (Steam ID, EOS ID, or player name) to banduration(required) - Ban duration in days (0 = permanent)reason(optional) - Reason for the ban
Example:
{
"name": "Ban Cheater",
"type": "action",
"config": {
"action_type": "ban_player",
"player_id": "${trigger_event.steam_id}",
"duration": 7,
"reason": "Cheating detected"
}
}Ban Player with Evidence (ban_player_with_evidence)
Bans a player and automatically links the triggering event as evidence in the database. This creates a complete audit trail with the event that caused the ban (chat messages, deaths, etc.) stored as evidence.
Configuration:
player_id(required) - Player ID (Steam ID, EOS ID, or player name) to banduration(required) - Ban duration in days (0 = permanent)reason(optional) - Reason for the banrule_id(optional) - Server rule UUID to associate with the ban
Supported Event Types for Evidence:
RCON_CHAT_MESSAGE- Chat message evidenceLOG_PLAYER_CONNECTED- Connection event evidenceLOG_PLAYER_DIED- Death event evidenceLOG_PLAYER_WOUNDED- Wound event evidenceLOG_PLAYER_DAMAGED- Damage event evidence
Features:
- Automatically creates ban record in database with UUID
- Links triggering event as evidence in ClickHouse
- Executes RCON ban command
- Kicks player immediately
- Returns ban_id, evidence_count, and success status
Example:
{
"name": "Ban with Offensive Language Evidence",
"type": "action",
"config": {
"action_type": "ban_player_with_evidence",
"player_id": "${trigger_event.steam_id}",
"duration": 7,
"reason": "Offensive language: ${trigger_event.message}",
"rule_id": "${workflow.variables.rule_id}"
}
}Notes:
- If the event_id is missing from the trigger context, the ban is created without evidence (graceful degradation)
- If the event type does not support evidence, a warning is logged and the ban proceeds without evidence
- Evidence links to ClickHouse records via the event UUID for complete audit trails
- The ban record can be queried via API with attached evidence for admin review and appeals
Warn Player (warn_player)
Sends a warning message to a specific player.
Configuration:
player_id(required) - Player ID (Steam ID or player name) to warnmessage(required) - Warning message
Example:
{
"name": "Warn Teamkiller",
"type": "action",
"config": {
"action_type": "warn_player",
"player_id": "${trigger_event.steam_id}",
"message": "Warning: Teamkilling is against server rules. Next violation will result in a kick."
}
}HTTP Request (http_request)
Makes an HTTP request to an external service.
Configuration:
url(required) - The URL to send the request tomethod(optional) - HTTP method (GET, POST, PUT, DELETE) - defaults to GETbody(optional) - Request body for POST/PUT requestsheaders(optional) - HTTP headers as key-value objectfail_on_error(optional) - Whether to fail the workflow on non-2xx status codes
Example:
{
"name": "Notify External API",
"type": "action",
"config": {
"action_type": "http_request",
"url": "https://api.example.com/events",
"method": "POST",
"headers": {
"Content-Type": "application/json",
"Authorization": "Bearer ${webhook_token}"
},
"body": "{\"event\": \"player_joined\", \"player\": \"${trigger_event.player_name}\"}",
"fail_on_error": true
}
}Webhook (webhook)
Sends a webhook notification with workflow and event data.
Configuration:
url(required) - Webhook URLpayload(optional) - Custom payload data to include (merged with default data)headers(optional) - Custom HTTP headers
Default Payload Data:
The webhook automatically includes:
workflow_id- Current workflow IDexecution_id- Current execution IDserver_id- Server IDtrigger_event- Full trigger event datavariables- Current workflow variablesmetadata- Workflow metadatatimestamp- Unix timestamp
Example:
{
"name": "Discord Notification",
"type": "action",
"config": {
"action_type": "webhook",
"url": "https://discord.com/api/webhooks/...",
"payload": {
"content": "Player **${trigger_event.player_name}** joined the server!",
"custom_field": "additional_data"
},
"headers": {
"Authorization": "Bearer ${webhook_token}"
}
}
}Discord Message (discord_message)
Sends a formatted message to Discord via webhook.
Configuration:
webhook_url(required) - Discord webhook URLmessage(required) - Message content (supports Discord markdown)username(optional) - Custom bot usernameavatar_url(optional) - Custom bot avatar URL
Example:
{
"name": "Discord Admin Alert",
"type": "action",
"config": {
"action_type": "discord_message",
"webhook_url": "https://discord.com/api/webhooks/...",
"message": "**Admin Alert**: ${trigger_event.player_name} was kicked for teamkilling",
"username": "Squad Aegis",
"avatar_url": "https://example.com/bot-avatar.png"
}
}Log Message (log_message)
Writes a message to the server logs.
Configuration:
message(required) - Message to loglevel(required) - Log level (debug,info,warn,error)
Example:
{
"name": "Log Player Event",
"type": "action",
"config": {
"action_type": "log_message",
"message": "Player ${trigger_event.player_name} triggered workflow ${metadata.workflow_name}",
"level": "info"
}
}Set Variable (set_variable)
Sets or updates a workflow variable.
Configuration:
variable_name(required) - Name of the variablevariable_value(required) - Value to assign
Example:
{
"name": "Track Last Player",
"type": "action",
"config": {
"action_type": "set_variable",
"variable_name": "last_player_name",
"variable_value": "${trigger_event.player_name}"
}
}Lua Script Action (lua_script)
Executes a custom Lua script as an action step with full access to workflow data. For complex scripting, prefer the dedicated lua step type (see Lua Script Steps).
Configuration:
script(required) - Lua script codetimeout_seconds(optional) - Maximum execution time (default: 30)
Example:
{
"name": "Custom Player Processing",
"type": "action",
"config": {
"action_type": "lua_script",
"script": "local player_name = workflow.trigger_event.player_name\nworkflow.log.info('Player ' .. player_name .. ' triggered workflow')\nworkflow.variables.processed_players = (workflow.variables.processed_players or 0) + 1",
"timeout_seconds": 10
}
}Condition Steps (condition)
Condition steps evaluate expressions and branch workflow execution based on the result. They support multiple conditions with AND/OR logic and can execute different sets of steps depending on whether the conditions pass or fail.
Important: Steps specified in true_steps or false_steps will only execute when called by the condition step, not when encountered in sequential workflow order. This prevents both branches from running.
Configuration Fields
-
logic(string): Combination operator for multiple conditions"AND"- All conditions must be true (default)"OR"- At least one condition must be true
-
conditions(array): List of conditions to evaluate. Each condition has:field(string): Path to the field to evaluate (e.g.,trigger_event.player_name)operator(string): Comparison operator (see Available Operators below)value(any): Value to compare againsttype(string): Data type (string,number,boolean,array,object)
-
true_steps(array): Steps to execute if conditions pass. Can contain step name strings (references to root-level steps) or full step objects (inline steps). Steps in this list are automatically skipped in sequential execution. -
false_steps(array): Steps to execute if conditions fail. Can contain step name strings (references to root-level steps) or full step objects (inline steps). Steps in this list are automatically skipped in sequential execution. -
continue_on_next_step_error(boolean): Whether to continue if a branch step fails. Default:false(stop on error).
Available Operators
equals- Exact matchnot_equals- Not equal togreater_than- Numeric greater thanless_than- Numeric less thangreater_than_or_equal- Numeric greater than or equalless_than_or_equal- Numeric less than or equalcontains- String contains substringnot_contains- String does not contain substringstarts_with- String starts withends_with- String ends within- Value in arraynot_in- Value not in arrayis_null- Value is nullis_not_null- Value is not nullregex_match- Matches regular expression
Inline Nested Steps
Steps can be defined directly within condition branches (inline) rather than at the root level. Inline steps are scoped to their branch and support all step types except nested conditions.
You can also mix inline steps with references to existing root-level steps within the same branch.
Visual Branch Indicators
In the workflow editor, condition steps label the true and false branches and show a step count for each.
Example: Basic Condition
{
"name": "Check Server Population",
"type": "condition",
"enabled": true,
"config": {
"logic": "AND",
"conditions": [
{
"field": "trigger_event.player_count",
"operator": "greater_than",
"value": 50,
"type": "number"
}
],
"true_steps": ["high-pop-action"],
"false_steps": ["low-pop-action"]
}
}Example: Inline Steps
{
"name": "Check Player VIP Status",
"type": "condition",
"enabled": true,
"config": {
"logic": "AND",
"conditions": [
{
"field": "trigger_event.steam_id",
"operator": "equals",
"value": "76561199047801300",
"type": "string"
}
],
"true_steps": [
{
"id": "inline-vip-broadcast",
"name": "VIP Welcome Broadcast",
"type": "action",
"enabled": true,
"config": {
"action_type": "admin_broadcast",
"message": "Welcome VIP ${trigger_event.player_name}!"
}
},
{
"id": "inline-vip-log",
"name": "Log VIP Join",
"type": "action",
"enabled": true,
"config": {
"action_type": "log_message",
"level": "info",
"message": "VIP player ${trigger_event.player_name} joined"
}
}
],
"false_steps": [
{
"id": "inline-normal-welcome",
"name": "Normal Welcome",
"type": "action",
"enabled": true,
"config": {
"action_type": "admin_broadcast",
"message": "Welcome ${trigger_event.player_name}!"
}
}
]
}
}Example: Player Whitelist Check with in and continue_on_next_step_error
{
"name": "Check Player Whitelist",
"type": "condition",
"enabled": true,
"config": {
"logic": "AND",
"conditions": [
{
"field": "trigger_event.player.steam_id",
"operator": "in",
"value": ["76561198012345678", "76561198087654321"],
"type": "array"
},
{
"field": "trigger_event.player.banned",
"operator": "equals",
"value": false,
"type": "boolean"
}
],
"true_steps": [
"log-whitelist-join",
"send-welcome-message",
"grant-permissions"
],
"false_steps": [
"log-non-whitelist-join",
"kick-player"
],
"continue_on_next_step_error": false
}
}Variable Steps (variable)
Variable steps set or update a workflow variable. Use them when a step's only job is to store a value; for variable changes alongside other work, use an action step with set_variable.
Configuration:
action_type(required) - Must beset_variablevariable_name(required) - Name of the variable to setvariable_value(required) - Value to assign (can be a scalar or object)
Example: Basic variable setting
{
"name": "Initialize Counter",
"type": "variable",
"enabled": true,
"config": {
"action_type": "set_variable",
"variable_name": "player_count",
"variable_value": 0
}
}Example: Object value
{
"name": "Track Last Player",
"type": "variable",
"enabled": true,
"config": {
"action_type": "set_variable",
"variable_name": "last_player_info",
"variable_value": {
"name": "${trigger_event.player_name}",
"steam_id": "${trigger_event.steam_id}",
"timestamp": "${trigger_event.time}"
}
}
}Delay Steps (delay)
Delay steps pause workflow execution for a specified duration.
Configuration:
delay_ms(required) - Delay duration in milliseconds
Example:
{
"name": "Wait 5 Seconds",
"type": "delay",
"config": {
"delay_ms": 5000
}
}Lua Script Steps (lua)
Lua steps run a script with the full workflow API. See Lua Scripting for the complete function reference. The script field is a string; \n separates lines.
Configuration:
script(required) - Lua script codetimeout_seconds(optional) - Maximum execution time (default: 30)
Example:
{
"name": "Advanced Player Analysis",
"type": "lua",
"enabled": true,
"config": {
"script": "local player_name = workflow.trigger_event.player_name\nlocal damage = workflow.trigger_event.damage or 0\n\nif damage > 50 then\n workflow.log.warn('High damage teamkill by ' .. player_name .. ': ' .. damage)\n workflow.variable.set('high_damage_tk_count', (workflow.variable.get('high_damage_tk_count') or 0) + 1)\nelse\n workflow.log.info('Low damage incident by ' .. player_name)\nend\n\nresult.player = player_name\nresult.damage = damage\nresult.severity = damage > 50 and 'high' or 'low'",
"timeout_seconds": 10
}
}Example: Player warning counter
{
"name": "Check Player Warning Count",
"type": "lua",
"enabled": true,
"config": {
"script": "local steam_id = workflow.trigger_event.steam_id\nlocal warnings = workflow.variables['warnings_' .. steam_id] or 0\nwarnings = warnings + 1\nworkflow.variables['warnings_' .. steam_id] = warnings\nresult.warning_count = warnings\nresult.should_kick = warnings >= 3"
}
}Variable Replacement
In most text fields, you can use variable replacement syntax to access dynamic data.
Syntax
${trigger_event.field_name}- Access trigger event data${metadata.field_name}- Access workflow metadata${variable_name}- Access workflow variables
Common Trigger Event Fields
Chat Messages:
${trigger_event.player_name}- Player who sent the message${trigger_event.message}- Message content${trigger_event.chat_type}- Type of chat (ChatAll, ChatTeam, etc.)
Player Events:
${trigger_event.player_name}- Player name${trigger_event.steam_id}- Player's Steam ID (may be empty for EOS-only players)${trigger_event.eos_id}- Player's Epic Online Services ID (may be empty for Steam-only players)
Admin Events:
${trigger_event.player_name}- Target player name${trigger_event.message}- Admin message or reason
Metadata Fields
${metadata.workflow_name}- Current workflow name${metadata.workflow_id}- Current workflow ID${metadata.execution_id}- Current execution ID${metadata.server_id}- Server ID${metadata.started_at}- Execution start time
Error Handling
Each step can have error handling configuration.
Step-Level Error Handling
{
"name": "Potentially Failing Operation",
"type": "action",
"enabled": true,
"config": {
"action_type": "rcon_command",
"command": "AdminBroadcast ${trigger_event.player_name} joined!"
},
"on_error": {
"action": "retry",
"max_retries": 3,
"retry_delay_ms": 1000
}
}Available Error Actions
continue- Continue to the next step despite the errorstop- Stop workflow execution immediatelyretry- Retry the failed step with specified parameters
Workflow-Level Error Handling
Set default error handling for the entire workflow:
{
"error_handling": {
"default_action": "continue",
"max_retries": 3,
"retry_delay_ms": 1000
}
}Error Handling Strategy
- Critical Steps: Use
stopto prevent further execution - Optional Steps: Use
continueto proceed despite failures - Unreliable Steps: Use
retrywith reasonable limits
Best Practices
- Use descriptive names for steps to make workflows easier to understand.
- Test variable replacement with simple log messages before using in complex actions.
- Set appropriate timeouts for Lua scripts and HTTP requests.
- Use error handling for steps that might fail (HTTP requests, external services).
- Log important events to help with debugging and monitoring.
- Keep Lua scripts simple and well-commented.
- Use variables to share data between steps effectively.
- Keep variable names consistent and meaningful; prefix per-player keys with the player identifier (e.g.,
warnings_${trigger_event.steam_id}). - For condition steps, ensure each step appears in either
true_stepsorfalse_steps, not both. - Minimize delays; only use them when necessary (e.g., waiting for a player to fully load).
Example Workflow
Complete example demonstrating multiple step types working together:
{
"version": "1.0",
"triggers": [
{
"name": "Teamkill Detection",
"event_type": "LOG_PLAYER_DIED",
"conditions": [
{
"field": "teamkill",
"operator": "equals",
"value": true,
"type": "boolean"
}
],
"enabled": true
}
],
"variables": {
"max_warnings": 3,
"warning_message": "Teamkilling is against server rules!"
},
"steps": [
{
"name": "Log Teamkill Incident",
"type": "action",
"enabled": true,
"config": {
"action_type": "log_message",
"message": "Teamkill detected: ${trigger_event.attacker_name} killed ${trigger_event.victim_name}",
"level": "warn"
}
},
{
"name": "Analyze Damage",
"type": "lua",
"enabled": true,
"config": {
"script": "local damage = workflow.trigger_event.damage or 0\nlocal attacker = workflow.trigger_event.attacker_name\n\nif damage > 75 then\n workflow.log.warn('High damage teamkill: ' .. damage)\n workflow.variable.set('action_required', 'kick')\nelse\n workflow.log.info('Low damage teamkill: ' .. damage)\n workflow.variable.set('action_required', 'warn')\nend\n\nresult.damage = damage\nresult.severity = damage > 75 and 'high' or 'low'"
}
},
{
"name": "Warn Player",
"type": "action",
"enabled": true,
"config": {
"action_type": "warn_player",
"player_id": "${trigger_event.attacker_name}",
"message": "${warning_message} This was a ${action_required} offense."
}
},
{
"name": "Notify Discord",
"type": "action",
"enabled": true,
"config": {
"action_type": "discord_message",
"webhook_url": "https://discord.com/api/webhooks/...",
"message": "**Teamkill Alert**\n**Attacker:** ${trigger_event.attacker_name}\n**Victim:** ${trigger_event.victim_name}\n**Damage:** ${trigger_event.damage}\n**Action:** ${action_required}"
}
}
]
}Last updated on