Incident Escalation
ivy.agent.incident-escalation · v1.0.0
ivyx
Takes a monitoring alert, has Jev rate its severity, and for a high one opens a GitHub issue, posts to Slack and mails on-call; otherwise logs it through your API.
Steps
In the order the agent's file lists them, each with where its inputs come from and the values the file fixes. A branch or a loop is a step too.
- 01Ivy NodeRate the severityivy.node.jev-score
Asks TypeSafe Jev where a text or JSON state sits on an ordered scale of 2 to 10 described levels and returns a score from 0 to the top level with the nearest level, its description and the confidence.
- takes
statefrom the agent's input,payload- set
instructionsRate the severity of this incident on these levels: low, medium, high. Customers unable to use a service is high; a r...levels["low","medium","high"]- secret
jev_api_keythe stored secret jev-api-key- returns
severity
- 02Ivy NodeWrite the titleivy.node.template-render
Fills {{ name }} placeholders in a text template from a mapping, and reports which names were missing.
- takes
valuesfrom the agent's input,payload- set
templateIncident on {{ service }}: {{ message }}on_missingempty- returns
title
- 03BranchOnly a high severity
Lets what follows run only when is.high of step 1 is true.
- 04Ivy NodeOpen the issueivy.node.http-request
Sends one HTTP request to a URL and returns the status, headers and body, parsed as JSON when the response is JSON.
Runs only on the then branch of step 3.
- takes
urlfrom the agent's input,issues_urljson.titlefrom step 2,textjson.bodyfrom the agent's input,payload.message- set
methodPOST- secret
tokenthe stored secret github-token- returns
escalated,issue_url
- 05Ivy NodeAlert the channelivy.node.http-request
Sends one HTTP request to a URL and returns the status, headers and body, parsed as JSON when the response is JSON.
Runs only on the then branch of step 3.
- takes
json.textfrom step 2,text- set
methodPOST- secret
urlthe stored secret slack-webhook-url
- 06Ivy NodePage on-callivy.node.email-send
Sends a plain-text or HTML email through an SMTP server in one step and returns the recipients it went to.
Runs only on the then branch of step 3.
- takes
subjectfrom step 2,textbodyfrom the agent's input,payload.messagefrom_addrfrom the agent's input,usernameusernamefrom the agent's inputtofrom the agent's inputsmtp_hostfrom the agent's inputsmtp_portfrom the agent's inputuse_tlsfrom the agent's input,smtp_tls- set
smtp_hostsmtp.gmail.comsmtp_port587use_tlstrue- secret
passwordthe stored secret mail-password- returns
sent
- 07Ivy NodeLog the alertivy.node.http-request
Sends one HTTP request to a URL and returns the status, headers and body, parsed as JSON when the response is JSON.
Runs only on the else branch of step 3.
- takes
urlfrom the agent's input,log_urljson.servicefrom the agent's input,payload.servicejson.levelfrom step 1,level_textjson.messagefrom the agent's input,payload.message- set
methodPOST- secret
tokenthe stored secret saas-api-key- returns
logged
Nodes it brings
Adding this agent in IVYX Studio adds these nodes with it. A node your workspace already has is kept as it is, even at another version.
What it touches
Collected from what each of its nodes declares, plus the model call when a step is a model turn. A declaration is the author's statement, and it is what policy rules select on.
What it needs
- A stored secret named
github-token. The agent's file carries the name, never the value. - A stored secret named
jev-api-key. The agent's file carries the name, never the value. - A stored secret named
mail-password. The agent's file carries the name, never the value. - A stored secret named
saas-api-key. The agent's file carries the name, never the value. - A stored secret named
slack-webhook-url. The agent's file carries the name, never the value.
Inputs
| Field | Type | Description |
|---|---|---|
| payloadrequired | object | The event your service posted: service, message and metrics of the alert. |
| issues_urlrequired | string | The repository's issues endpoint, such as https://api.github.com/repos/<owner>/<repo>/issues. The stand-in's address is used in the cases. |
| log_urlrequired | string | Your API's log endpoint; a POST records an alert that was not escalated. The stand-in's address is used in the cases. |
| usernamerequired | string | The sending address, which is the login name; its password is the stored secret mail-password. |
| torequired | string | The on-call address. |
| smtp_host | string | The SMTP server; smtp.gmail.com when left out. |
| smtp_port | integer | The SMTP port; 587 when left out. |
| smtp_tls | boolean | Upgrade SMTP with STARTTLS; on when left out. |
Outputs
| Field | Type | Description |
|---|---|---|
| severity | string | low, medium or high. |
| escalated | boolean | True once GitHub accepted the issue; absent when not escalated. |
| issue_url | string | The issue's page; absent when not escalated. |
| sent | boolean | True once the server accepted the mail to on-call; absent when not escalated. |
| logged | boolean | True once your API recorded the alert; absent when escalated. |
| title | string | The one-line title of the alert. |
Tests
2 of 2 test cases passed on Oct 7, 2026, in the publisher's own environment, before this version was published. The registry keeps that record; it does not run the cases again.
Requires: python:3.9, the stored secret jev-api-key, a GitHub token stored as the secret github-token, a Slack incoming webhook URL stored as the secret slack-webhook-url, your own API's key stored as the secret saas-api-key, an SMTP server (Gmail unless smtp_host says otherwise) with the sending account's password stored as the secret mail-password, for the cases, GreenMail on 127.0.0.1:3025 (mkdata-mail.py), for the cases, the stand-in API on 127.0.0.1:18910 (api-standin.py), which the secrets crm-1, gh-1, saas-1 and the stand-in's /slack/hook address satisfy
- payments-down
Every payment request failing is high: an issue, a Slack alert and a page to on-call.
- given
smtp_host127.0.0.1smtp_port3025smtp_tlsfalsepayload{"service":"payments-api","message":"100% of requests failing with 503 since 09:12 UTC; customers cannot pay","metric...issues_urlhttp://127.0.0.1:18910/repos/acme/app/issueslog_urlhttp://127.0.0.1:18910/logusernameagent@ivyx.testtoagent@ivyx.test- expects
severityequals highescalatedequals trueissue_urlmatches issues/\d+$sentequals truetitlematches ^Incident on payments-api
- disk-warning
A disk at 70% with no customer impact is low or medium: only the log hears.
- given
smtp_host127.0.0.1smtp_port3025smtp_tlsfalsepayload{"service":"reports-worker","message":"Disk usage at 70% on worker-3; no customer impact, cleanup scheduled","metrics...issues_urlhttp://127.0.0.1:18910/repos/acme/app/issueslog_urlhttp://127.0.0.1:18910/logusernameagent@ivyx.testtoagent@ivyx.test- expects
severitymatches ^(low|medium)$loggedequals true