Skip to main content

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 ([email protected])"

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