Documentation Index
Fetch the complete documentation index at: https://docs.rootly.com/llms.txt
Use this file to discover all available pages before exploring further.
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 (john@example.com)"
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