Overview
Edge Connectors use Liquid templates to dynamically substitute values from events, user parameters, and environment variables into your action configurations.
Templates allow you to:
- Access event data (alerts, incidents, etc.)
- Use user-provided parameters from manual triggers
- Reference environment variables securely
- Transform data with filters
Edge Connectors use the osteele/liquid library, a Go implementation of Shopify’s Liquid template language.
Basic Syntax
Simple Fields
Access top-level fields directly:
{{ id }} # Event ID
{{ summary }} # Alert/incident summary
{{ status }} # Current status
{{ title }} # Incident title
Nested Fields
Use dot notation for nested objects:
{{ labels.severity }} # Alert severity label
{{ data.host }} # Custom monitoring data
{{ severity.name }} # Incident severity object
{{ triggered_by.email }} # User who triggered action
Array Access
Access array elements by index or using helpers:
{{ services[0].name }} # First service (by index)
{{ services.first.name }} # First service (helper)
{{ services.last.slug }} # Last service (helper)
{{ environments[0].slug }} # First environment
Environment Variables
Securely access environment variables:
{{ env.API_KEY }} # From REC_API_KEY or API_KEY
{{ env.AWS_REGION }} # From REC_AWS_REGION or AWS_REGION
{{ env.WEBHOOK_URL }} # From REC_WEBHOOK_URL or WEBHOOK_URL
Store sensitive values like API keys and tokens in environment variables, never in action configuration files.
Event Data Access
Alert Events
Common fields available in alert events:
{{ id }} # Alert UUID
{{ summary }} # Alert summary text
{{ status }} # open, acknowledged, resolved
{{ source }} # datadog, pagerduty, etc.
{{ labels.severity }} # Severity from monitoring system
{{ data.host }} # Custom monitoring data
{{ services[0].name }} # Affected service
{{ environments[0].slug }} # Environment (production, etc.)
{{ started_at }} # When alert started
Incident Events
Common fields available in incident events:
{{ id }} # Incident UUID
{{ sequential_id }} # Incident number (42, 43, etc.)
{{ title }} # Incident title
{{ summary }} # Incident summary
{{ status }} # started, mitigated, resolved
{{ severity.name }} # SEV1, SEV2, etc.
{{ severity.slug }} # sev1, sev2, etc.
{{ services | map: 'name' }} # All affected services
{{ environments[0].name }} # Environment name
{{ detected_at }} # When detected
{{ mitigated_at }} # When mitigated
{{ resolved_at }} # When resolved
Action Trigger Events
Fields available when users manually trigger actions:
{{ entity_id }} # Alert or incident ID
{{ action.name }} # Action display name
{{ action.slug }} # Action identifier
{{ parameters.service_name }} # User input parameter
{{ parameters.environment }} # User input parameter
{{ triggered_by.id }} # User ID
{{ triggered_by.name }} # User name
{{ triggered_by.email }} # User email
Filters
Filters transform values using the pipe (|) operator.
Array Filters
map - Extract property from objects:
{{ services | map: "name" }}
# [{name: "DB"}, {name: "API"}] → ["DB", "API"]
join - Combine array elements:
{{ services | map: "name" | join: ", " }}
# ["DB", "API", "Cache"] → "DB, API, Cache"
first - Get first element:
{{ services | first }}
# Returns first service object
last - Get last element:
{{ services | last }}
# Returns last service object
sort - Sort alphabetically:
{{ names | sort }}
# ["charlie", "alice", "bob"] → ["alice", "bob", "charlie"]
uniq - Remove duplicates:
{{ items | uniq }}
# [1, 2, 2, 3, 1] → [1, 2, 3]
compact - Remove nil values:
{{ items | compact }}
# [1, nil, 2, nil, 3] → [1, 2, 3]
reverse - Reverse order:
{{ items | reverse }}
# [1, 2, 3] → [3, 2, 1]
String Filters
upcase - Convert to uppercase:
{{ status | upcase }}
# "open" → "OPEN"
downcase - Convert to lowercase:
{{ severity | downcase }}
# "CRITICAL" → "critical"
capitalize - Capitalize first letter:
{{ name | capitalize }}
# "john doe" → "John doe"
default - Provide fallback value:
{{ field | default: "N/A" }}
# If field is empty → "N/A"
truncate - Shorten text:
{{ summary | truncate: 50 }}
# "Very long summary text..." → "Very long summary text..."
replace - Replace all occurrences:
{{ text | replace: "foo", "bar" }}
# "foo foo" → "bar bar"
remove - Remove all occurrences:
{{ severity | remove: "SEV" }}
# "SEV1" → "1"
strip - Remove whitespace:
{{ text | strip }}
# " hello " → "hello"
append - Add to end:
{{ name | append: ".txt" }}
# "file" → "file.txt"
prepend - Add to beginning:
{{ name | prepend: "prefix-" }}
# "name" → "prefix-name"
split - Split into array:
{{ "a,b,c" | split: "," }}
# "a,b,c" → ["a", "b", "c"]
Number Filters
plus - Add:
{{ count | plus: 1 }}
# 5 → 6
minus - Subtract:
{{ count | minus: 2 }}
# 5 → 3
times - Multiply:
{{ value | times: 10 }}
# 5 → 50
divided_by - Divide:
{{ value | divided_by: 2 }}
# 10 → 5
modulo - Remainder:
{{ value | modulo: 3 }}
# 10 → 1
abs - Absolute value:
{{ value | abs }}
# -5 → 5
round - Round number:
{{ value | round: 2 }}
# 3.14159 → 3.14
ceil - Round up:
{{ value | ceil }}
# 3.2 → 4
floor - Round down:
{{ value | floor }}
# 3.8 → 3
Date Filters
date - Format timestamp:
{{ started_at | date: "%Y-%m-%d %H:%M:%S" }}
# "2025-10-26T21:30:00Z" → "2025-10-26 21:30:00"
{{ started_at | date: "%B %d, %Y" }}
# "2025-10-26T21:30:00Z" → "October 26, 2025"
Common date format codes:
%Y - Year (2025)
%m - Month (01-12)
%d - Day (01-31)
%H - Hour 24h (00-23)
%M - Minute (00-59)
%S - Second (00-59)
%B - Full month name (January)
%b - Short month name (Jan)
Real-World Examples
Example 1: Alert Notification
Format a Slack message with alert details:
body: |
{
"text": ":warning: New Alert",
"attachments": [{
"color": "danger",
"fields": [
{"title": "Summary", "value": "{{ summary }}", "short": false},
{"title": "Severity", "value": "{{ labels.severity | upcase }}", "short": true},
{"title": "Host", "value": "{{ data.host | default: 'unknown' }}", "short": true},
{"title": "Services", "value": "{{ services | map: 'name' | join: ', ' }}", "short": false},
{"title": "Environment", "value": "{{ environments.first.name }}", "short": true},
{"title": "Time", "value": "{{ started_at | date: '%Y-%m-%d %H:%M' }}", "short": true}
]
}]
}
Example 2: Incident Summary
Create a concise incident summary:
message: "[{{ severity.name }}] {{ title }} - {{ services | map: 'name' | join: ', ' }} ({{ environments.first.slug }})"
# Result: "[SEV1] API Gateway Outage - API Gateway, Auth Service (production)"
Example 3: Script Parameters
Pass structured data to a script:
parameters:
incident_id: "{{ id }}"
incident_number: "{{ sequential_id }}"
severity: "{{ severity.slug }}"
services: "{{ services | map: 'slug' | join: ',' }}"
environment: "{{ environments.first.slug }}"
triggered_by: "{{ triggered_by.email | default: 'system' }}"
timestamp: "{{ started_at | date: '%Y-%m-%d %H:%M:%S' }}"
Example 4: Conditional Values
Use defaults for optional fields:
parameters:
reason: "{{ parameters.reason | default: 'Manual action triggered' }}"
environment: "{{ parameters.environment | default: 'production' }}"
force: "{{ parameters.force_restart | default: false }}"
host: "{{ data.host | default: 'localhost' }}"
Chain multiple filters:
# Extract, sort, and format service names
services_list: "{{ services | map: 'name' | sort | join: ' | ' | upcase }}"
# Result: "API GATEWAY | AUTH SERVICE | DATABASE"
# Format severity without prefix
severity_number: "{{ severity.name | remove: 'SEV' }}"
# "SEV1" → "1"
Advanced Patterns
Chaining Filters
Combine multiple filters in sequence:
{{ services | map: "name" | sort | uniq | join: ", " | upcase }}
# Extract names → sort → remove duplicates → join → uppercase
Nested Array Access
Access deeply nested data:
{{ services[0].tags[0] }} # First service's first tag
{{ data.metrics.values[5] }} # Sixth metric value
{{ environments.first.config.region }} # Environment config
Safe Navigation
Liquid handles missing values gracefully:
{{ missing.field }} # Returns empty string ""
{{ array[999].name }} # Returns "" (out of bounds)
{{ undefined | default: "N/A" }} # Returns "N/A"
Common Patterns
Service List
services: "{{ services | map: 'name' | join: ', ' }}"
# "Database, API Gateway, Cache"
Environment Detection
env: "{{ environments.first.slug | default: 'unknown' }}"
# "production"
severity: "{{ labels.severity | upcase | default: 'UNKNOWN' }}"
# "CRITICAL"
User Context
user: "{{ triggered_by.name }} ({{ triggered_by.email }})"
# "John Doe ([email protected])"
time: "{{ started_at | date: '%Y-%m-%d %H:%M:%S UTC' }}"
# "2025-10-26 21:30:00 UTC"
Limitations
To keep templates simple and secure, the following Liquid features are not supported:
- No logic tags:
{% if %}, {% unless %}, {% case %} not supported
- No loops:
{% for %} not supported - use filters like map and join instead
- No custom tags: Only
{{ }} output tags are supported
- No assignments:
{% assign %} not supported
Use filters and the default filter for conditional logic:
# Instead of {% if field %}{{ field }}{% else %}N/A{% endif %}
# Use:
{{ field | default: "N/A" }}
Tips & Best Practices
1. Use Default Filter
Always provide fallback values for optional fields:
{{ data.host | default: "unknown" }}
{{ parameters.timeout | default: 30 }}
For arrays of objects, use map + join:
{{ services | map: "name" | join: ", " }}
3. Test Templates
Test with sample event data before deploying:
- Use the Event Examples for reference payloads
- Verify templates produce expected output
- Handle edge cases (empty arrays, missing fields)
4. Keep It Simple
Complex logic belongs in scripts, not templates:
# Good: Simple data extraction
service: "{{ services.first.name }}"
# Bad: Complex transformation (do this in a script instead)
# Avoid overly complex filter chains
5. Environment Variables for Secrets
Never hardcode secrets in templates:
# Good
Authorization: "Bearer {{ env.API_TOKEN }}"
# Bad
Authorization: "Bearer sk-1234567890abcdef"
Use multiline strings for JSON/YAML bodies:
body: |
{
"field1": "{{ value1 }}",
"field2": "{{ value2 }}"
}
Troubleshooting
Template Returns Empty String
- Check field name spelling
- Verify field exists in event payload (see Event Examples)
- Use
default filter: {{ field | default: "missing" }}
Array Access Fails
- Verify array is not empty
- Use
.first or .last helpers for safety
- Check array index is in bounds
Filter Not Working
- Verify filter name is correct
- Check filter arguments (some require arguments:
{{ value | round: 2 }})
- Ensure input type matches filter (can’t
upcase a number)
Environment Variable Not Found
- Verify variable is set in environment
- Check variable name (case-sensitive)
- Edge Connector supports both
REC_ prefix and plain names
Next Steps