Skip to main content

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' }}"

Example 5: Complex Transformation

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 Formatting

severity: "{{ labels.severity | upcase | default: 'UNKNOWN' }}"
# "CRITICAL"

User Context

user: "{{ triggered_by.name }} ({{ triggered_by.email }})"
# "John Doe (john@example.com)"

Timestamp Formatting

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 }}

2. Extract Then Join

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"

6. Format for Readability

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