# Watcher: How can I send email alert only when the difference of actual and typical is greater than X

**URL:** <https://discuss.elastic.co/t/watcher-how-can-i-send-email-alert-only-when-the-difference-of-actual-and-typical-is-greater-than-x/284304>\
**Category:** Elasticsearch\
**Tags:** elastic-stack-alerting, painless\
**Created:** [September 15, 2021, 3:34pm UTC](https://discuss.elastic.co/t/watcher-how-can-i-send-email-alert-only-when-the-difference-of-actual-and-typical-is-greater-than-x/284304 "2021-09-15T15:34:53Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![ElasticLiver](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/elasticliver/32/64869_2.png) [@ElasticLiver](https://discuss.elastic.co/u/ElasticLiver)\
**Post date:** [September 15, 2021, 3:34pm UTC](https://discuss.elastic.co/t/watcher-how-can-i-send-email-alert-only-when-the-difference-of-actual-and-typical-is-greater-than-x/284304/1 "2021-09-15T15:34:53Z")

</div>

How can I send email alert only when the difference of actual and typical is greater than X?

this is my watch:

```auto
{
    "trigger": {
      "schedule": {
        "interval": "5m"
      }
    },
    "input": {
      "search": {
        "request": {
          "search_type": "query_then_fetch",
          "indices": [
            ".ml-anomalies-*"
          ],
          "rest_total_hits_as_int": true,
          "body": {
            "query": {
              "bool": {
                "filter": [
                  {
                    "range": {
                      "timestamp": {
                        "gte": "now-35m"
                      }
                    }
                  },
                  {
                    "term": {
                      "result_type": "record"
                    }
                  },
                  {
                    "term": {
                      "job_id": "{{ctx.metadata.job_id}}"
                    }
                  },
                  {
                    "range": {
                      "record_score": {
                        "gte": "{{ctx.metadata.min_record_score}}"
                      }
                    }
                  }
                ]
              }
            },
            "script_fields": {
              "start": {
                "script": {
                  "lang": "painless",
                  "source": """
LocalDateTime.ofEpochSecond((doc["timestamp"].value.getMillis()-((doc["bucket_span"].value * 1000) * params.padding)) / 1000, 0, ZoneOffset.UTC).toString()+":00.000Z"
""",
                  "params": {
                    "padding": 10
                  }
                }
              },
              "end": {
                "script": {
                  "lang": "painless",
                  "source": """
LocalDateTime.ofEpochSecond((doc["timestamp"].value.getMillis()+((doc["bucket_span"].value * 1000) * params.padding)) / 1000, 0, ZoneOffset.UTC).toString()+":00.000Z"
""",
                  "params": {
                    "padding": 10
                  }
                }
              },
              "timestamp_epoch": {
                "script": {
                  "lang": "painless",
                  "source": """doc["timestamp"].value.getMillis()/1000"""
                }
              },
              "timestamp_iso8601": {
                "script": {
                  "lang": "painless",
                  "source": """doc["timestamp"].value"""
                }
              },
              "split": {
                "script": {
                  "lang": "painless",
                  "source": """doc["partition_field_value"]"""
                }
              },
              "by_field": {
                "script": {
                  "lang": "painless",
                  "source": """doc["by_field_value"]"""
                }
              },
              "actual": {
                "script": {
                  "lang": "painless",
                  "source": """Math.round(doc["actual"].value)"""
                }
              },
              "typical": {
                "script": {
                  "lang": "painless",
                  "source": """Math.round(doc["typical"].value)"""
                }
              },
              "score": {
                "script": {
                  "lang": "painless",
                  "source": """Math.round(doc["record_score"].value)"""
                }
              }
            }
          }
        }
      }
    },
    "condition": {
      "compare": {
        "ctx.payload.hits.total": {
          "gt": 0
        }
      }
    },
    "actions": {
      "send_email": {
        "throttle_period_in_millis": 900000,
        "transform": {
          "script": {
            "source": """
          return ctx.payload.hits.hits.stream().map(p -> [
            'source':p.fields.split.0,
            'disco':p.fields.by_field.0,
            'score':p.fields.score.0,
            'actual':p.fields.actual.0,
            'typical':p.fields.typical.0,
            'timestamp':p.fields.timestamp_iso8601.0,
            'start':p.fields.start.0,
            'end':p.fields.end.0
            ])
            .collect(Collectors.toList());
""",
            "lang": "painless"
          }
        },
        "email": {
          "profile": "standard",
          "from": "noreply-ml@mymail.cl",
          "to": [
            "dsto@mail.com"
          ],
          "subject": "ML Watcher Alert (record score alert)",
          "body": {
            "html": """<html>
  <body>
    <strong>
      Machine Learning Alert </strong>
    <br />
    <br />

  {{#ctx.payload._value}} <strong>
      Job
    </strong>: {{ctx.metadata.job_id}}
    <br />

    <strong>
      Time
    </strong>: {{timestamp}}
    <br />
    
    <strong>
      Record score
    </strong>: {{score}}
    <br />
  <strong>
      Host
    </strong>: {{source}}
    <br />
  <strong>
      Source
    </strong>: {{disco}}
    <br />

 <strong>
      actual
    </strong>: {{actual}}
    <br />

  <strong>
      typical
    </strong>: {{typical}}
    <br />

 <br />

     <a href="https://mykibana.com/app/ml#/timeseriesexplorer?_g=(ml:(jobIds:!({{ctx.metadata.job_id}})),refreshInterval:(pause:!t,value:0),time:(from:'{{start}}',to:'{{end}}'))&_a=(mlSelectInterval:(display:Auto,val:auto),mlSelectSeverity:(color:%23d2e9f7,display:warning,val:0),mlTimeSeriesExplorer:(entities:(disco.keyword:{{disco}},hostname.keyword:{{source}})))">              
        >>> Abrir en Anomaly Explorer.
         </a>
    <br />
    <br />

  {{/ctx.payload._value}}

  </body>
</html>
"""
          }
        }
      }
    },
    "metadata": {
      "min_record_score": 75,
      "job_id": "datastore-util-per"
    }
  }

```

---

<div class="post-metadata">

**Author:** ![richcollier](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/richcollier/32/115035_2.png) [@richcollier](https://discuss.elastic.co/u/richcollier)\
**Post date:** [September 16, 2021, 12:45pm UTC](https://discuss.elastic.co/t/watcher-how-can-i-send-email-alert-only-when-the-difference-of-actual-and-typical-is-greater-than-x/284304/2 "2021-09-16T12:45:02Z")

</div>

See this example: [https://gist.github.com/richcollier/e03a012b3ebc426e0720f238374ea3ad](https://gist.github.com/richcollier/e03a012b3ebc426e0720f238374ea3ad)

---

<div class="post-metadata">

**Author:** ![ElasticLiver](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/elasticliver/32/64869_2.png) [@ElasticLiver](https://discuss.elastic.co/u/ElasticLiver)\
**Post date:** [September 16, 2021, 2:44pm UTC](https://discuss.elastic.co/t/watcher-how-can-i-send-email-alert-only-when-the-difference-of-actual-and-typical-is-greater-than-x/284304/3 "2021-09-16T14:44:50Z")

</div>

Thanks again Rich, what you do on your watcher is similar to apply this rule in ML?

![image](https://us1.discourse-cdn.com/elastic/original/3X/9/f/9fbbcebfbed9f92cdbf3960a3be95dd6e814ecbb.png)

---

<div class="post-metadata">

**Author:** ![richcollier](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/richcollier/32/115035_2.png) [@richcollier](https://discuss.elastic.co/u/richcollier)\
**Post date:** [September 16, 2021, 4:17pm UTC](https://discuss.elastic.co/t/watcher-how-can-i-send-email-alert-only-when-the-difference-of-actual-and-typical-is-greater-than-x/284304/4 "2021-09-16T16:17:59Z")

</div>

in the end, the outcome is similar. Obviously if you use a rule in ML, the anomaly record never gets created in the first place. Otherwise, you can use the Watcher logic to do the "filtering".

---

<div class="post-metadata">

**Author:** ![system](https://us1.discourse-cdn.com/elastic/original/3X/1/a/1ac57faf039f6b580b3f104ef42a2a89e41014de.png) [@system](https://discuss.elastic.co/u/system)\
**Post date:** [October 14, 2021, 4:18pm UTC](https://discuss.elastic.co/t/watcher-how-can-i-send-email-alert-only-when-the-difference-of-actual-and-typical-is-greater-than-x/284304/5 "2021-10-14T16:18:50Z")

</div>

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.
