This template runs Autopilot on a New Relic alert and sends the root cause analysis as a formatted HTML email. If Autopilot returns no usable response, the workflow sends an error notice instead. For shared prerequisites and configuration notes, see Autopilot RCA workflow templates.
Before using this workflow, ensure you have:
- An Email destination configured in New Relic — see Create destinations
- The destination's UUID
This workflow completes the following steps:
- Runs Autopilot with a root cause analysis prompt scoped to the alert issue ID
- Checks whether Autopilot returned a usable response
- If Autopilot succeeds, formats the response as HTML and sends it to the email destination
- If Autopilot fails, sends an error email with the issue ID
This workflow uses newrelic.autopilot.run and newrelic.notification.sendEmail.
name: autopilotEmailRCAdescription: Automatically analyze New Relic alerts with Autopilot and send a formatted incident analysis report via emailworkflowInputs: issueId: type: String required: true validations: - errorMessage: Must be a valid New Relic issue ID in UUID format pattern: '^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$' type: regex emailDestinationId: type: String required: true validations: - pattern: ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$ errorMessage: "Invalid format. Please provide a valid UUID." type: regexsteps: - name: runAutopilot type: action action: newrelic.autopilot.run version: 1 inputs: prompt: >- Investigate New Relic issue ${{ .workflowInputs.issueId }}. Produce an incident analysis report covering: what triggered the alert, likely root cause, affected entities, supporting evidence, and recommended next steps. IMPORTANT: Output your final answer DIRECTLY as clean, basic HTML. Use <h2> for section headers, <b> for bold text, <p> for paragraphs, <code> for logs/IDs, and standard <table>/<tr>/<td> tags with simple borders for tabular data. Do NOT use markdown. selectors: - name: agentResponse expression: .response.finalAnswer - name: agentResponseBlocks expression: '(.response.finalAnswer // "") as $r | (if ($r|type)=="string" then ($r | try fromjson catch null) else $r end) as $json | if ($json|type)=="object" and ($json.card.body|type)=="array" then $json.card.body elif ($json|type)=="array" then $json elif ($json|type)=="object" and ($json.text|type)=="string" then [{"type":"Text","value":$json.text}] else [{"type":"Text","value":($r|tostring)}] end' ignoreErrors: true
- name: checkAutopilotResponse type: switch switch: - condition: ${{ (.steps.runAutopilot.outputs.agentResponse // "") != "" }} next: sendRcaEmail next: sendAutopilotError
- name: sendRcaEmail type: action action: newrelic.notification.sendEmail version: 1 inputs: destinationId: ${{ .workflowInputs.emailDestinationId }} subject: Autopilot RCA Report - Issue ${{ .workflowInputs.issueId }} message: '${{ def sanitizeHtml: gsub("(?i)<script\\b[^>]*>[\\s\\S]*?</\\s*script\\s*>"; "") | gsub("(?i)<\\s*script\\b[^>]*/?\\s*>"; "") | gsub("(?i)<style\\b[^>]*>[\\s\\S]*?</\\s*style\\s*>"; "") | gsub("(?i)<\\s*(iframe|object|embed|link|meta|form|svg)\\b[^>]*/?\\s*>"; "") | gsub("(?i)</\\s*(iframe|object|embed|form|svg)\\s*>"; "") | gsub("(?i)\\son\\w+\\s*=\\s*[^\\s>]+"; "") | gsub("(?i)javascript\\s*:"; ""); (.steps.runAutopilot.outputs.agentResponseBlocks // []) as $blocks | [ $blocks[] | if .type == "CodeBlock" then "<pre><code>" + ((.code // "") | gsub("&";"&") | gsub("<";"<") | gsub(">";">")) + "</code></pre>" else ((.value // "") | gsub("\\*\\*";"") | sanitizeHtml) as $clean | [ $clean / "\n" | .[] | select((. | gsub("^\\s+|\\s+$";"")) != "") ] as $lines | (reduce $lines[] as $line ([]; ($line | test("^\\s*\\|?\\s*:?-+:?\\s*(\\|\\s*:?-+:?\\s*)+\\|?\\s*$")) as $isSep | ($line | contains("|")) as $isPipe | if $isSep then . elif $isPipe then if (length > 0 and .[-1].type == "table") then .[0:-1] + [ .[-1] + {rows: (.[-1].rows + [$line])} ] else . + [ {type:"table", rows: [$line]} ] end else . + [ {type:"line", value: $line} ] end )) as $rawSegments | [ $rawSegments[] | if .type == "table" and (.rows | length) < 2 then {type:"line", value: .rows[0]} else . end ] as $segments | [ $segments[] | if .type == "table" then "<table style=\"width:100%;border-collapse:collapse;font-family:Arial,sans-serif;\">" + ([ .rows | to_entries[] | . as {key: $i, value: $row} | ($row | sub("^\\s*\\|";"") | sub("\\|\\s*$";"")) as $trimmed | [ $trimmed / "|" | .[] | gsub("^\\s+|\\s+$";"") ] as $cells | (if $i == 0 then "th" else "td" end) as $tag | (if $i == 0 then "text-align:left;padding:8px;border-bottom:2px solid #ccc;" else "padding:8px;border-bottom:1px solid #eee;" end) as $style | "<tr>" + ([ $cells[] | "<" + $tag + " style=\"" + $style + "\">" + . + "</" + $tag + ">" ] | join("")) + "</tr>" ] | join("")) + "</table>" else .value as $line | ($line | sub("^\\s+";"")) as $t | if ($t | startswith("<")) then $line elif ($line | startswith("### ")) then "<h3>" + ($line | ltrimstr("### ")) + "</h3>" elif ($line | startswith("## ")) then "<h2>" + ($line | ltrimstr("## ")) + "</h2>" elif ($line | startswith("# ")) then "<h2>" + ($line | ltrimstr("# ")) + "</h2>" elif ($line | startswith("- ")) then "<p>• " + ($line | ltrimstr("- ")) + "</p>" else "<p>" + $line + "</p>" end end ] | join("\n") end ] | join("\n") }}' next: end
- name: sendAutopilotError type: action action: newrelic.notification.sendEmail version: 1 inputs: destinationId: ${{ .workflowInputs.emailDestinationId }} subject: Autopilot RCA Failed - Issue ${{ .workflowInputs.issueId }} message: >- Autopilot could not produce findings for issue ${{ .workflowInputs.issueId }}. Please investigate manually. next: endHow it works
The runAutopilot step uses two selectors to handle finalAnswer shape variability at the data-extraction layer:
agentResponsecaptures the rawfinalAnswervalue. ThecheckAutopilotResponsestep uses this to determine whether Autopilot returned anything.agentResponseBlocksnormalizes the value into an array of typed block objects, handling three possiblefinalAnswershapes: a plain string, a JSON-encoded object with a.card.bodyarray, and a bare array. This gives thesendRcaEmailstep a consistent structure to iterate over regardless of how Autopilot formatted its response.
sendRcaEmail processes the blocks inline:
CodeBlockitems are rendered as<pre><code>with HTML entities escaped.- Other blocks pass through
sanitizeHtml, which strips<script>,<style>, inline event handlers, and other potentially unsafe markup. - Markdown-style pipe tables are detected and converted to
<table>elements with basic inline styles. - Heading prefixes (
#,##,###) and bullet markers (-) are converted to the corresponding HTML tags. - All remaining lines are wrapped in
<p>tags.
Unlike the Alert RCA with Autopilot and Slack template, HTML formatting is handled entirely within the sendRcaEmail step — no separate assign step is needed.
Related topics
Autopilot RCA workflow templates
Shared prerequisites and configuration notes for all Autopilot RCA templates
Autopilot overview
Learn how Autopilot investigates alerts and incidents
Conditional logic
Use switch statements to branch on success or failure
Send email notifications
Full reference for the sendEmail action
Create destinations
Configure the Email destination required by this workflow
Alert RCA with Autopilot and Slack
Post RCA findings to Slack instead of email
Troubleshoot Workflow Automation
Solutions for common Autopilot and workflow integration issues