# Custom monitors

{% hint style="warning" %}
Custom SQL Monitors are available for **Enterprise** users and can be configured via both the **Soda Cloud UI** and the **Monitoring Configuration API**.
{% endhint %}

**Custom SQL Monitors** enable you to define monitoring logic using **your own SQL queries**. This is ideal when built-in Soda metrics or anomaly checks don’t meet your needs; for example, when you must aggregate data across multiple tables, compute ratios, or detect anomalies in grouped datasets.

A **Custom SQL Monitor** runs your SQL query against your connected data source and evaluates its results on a schedule, just like any other Soda monitor.

This feature can be used to:

* Define metrics using **custom aggregations or joins**.
* **Compute grouped results** (e.g., `GROUP BY` customer, institution, or region).
* Apply **filters, CTEs, and where clauses** to narrow down data.
* Integrate results with **notification rules** to alert your team when certain conditions are met.

## How to create a custom SQL monitor

**Example scenario:** an organization needs to monitor **daily incidents** per **borough** and **reason** in their `Bus Breakdowns and Delays` dataset, and flag unusual spikes/drops via notification rules.

The goal is to know which boroughs are the ones suffering the most incidents and why that's happening.

#### Prerequisites

* **Enterprise** plan.
* A dataset connected in Soda Cloud.
* An **API token** with permission to **author** monitors (if creating [#in-the-column-monitoring-configuration-api](#in-the-column-monitoring-configuration-api "mention")).

### In the Soda Cloud UI

1. Navigate to **Datasets → Custom Monitors** at the bottom of the page. Click on **Add Column Monitor**.

   <figure><img src="https://1123167021-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FA2PmHkO5cBgeRPdiPPOG%2Fuploads%2FYQhldxcMuMUIZYF39lj5%2Fimage.png?alt=media&#x26;token=417ec643-21f7-4498-91d8-8caf3a63d5d1" alt=""><figcaption></figcaption></figure>

2. **Name** your custom monitor and **provide the custom SQL query**. \
   In this case, we are monitoring **incident count** by **borough** and **reason**.

   <figure><img src="https://1123167021-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FA2PmHkO5cBgeRPdiPPOG%2Fuploads%2FvvlKkU7sZRJmJFHVGgd5%2Fimage.png?alt=media&#x26;token=860903f0-4947-4b0e-9b8d-29c0eb9355d5" alt="" width="563"><figcaption></figcaption></figure>

3. Provide a **Result metric** and a **Valid range**, and define a **Threshold strategy**.\
   In this case, the **result metric** is `incident_count`; we want to **group by** `Boro` and `Reason`, and the **valid range** cannot be negative, so the minimum value is `0`. Both **Upper range** and **Lower range** anomaly detection are enabled to catch unusual spikes/drops per group.

   <figure><img src="https://1123167021-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FA2PmHkO5cBgeRPdiPPOG%2Fuploads%2FXodSoKrnkslP01zSXSe0%2Fimage.png?alt=media&#x26;token=101088c1-2b4b-4923-b36d-707b806ea598" alt="" width="563"><figcaption></figcaption></figure>

4. Click on **Add Monitor** on the top right.

The monitor will now be visible at the bottom of the Metric Monitoring dashboard.

<figure><img src="https://1123167021-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FA2PmHkO5cBgeRPdiPPOG%2Fuploads%2FGrH5WS6RMkAPw3zXUI85%2Fimage.png?alt=media&#x26;token=e9b3e0f9-fafc-41ef-8c44-b0680ed722b5" alt=""><figcaption></figcaption></figure>

This monitor will:

* Run daily and compute `incident_count` for every `(Boro, Reason)` pair within the partitioned time window.
* Store grouped results so you can see which areas and causes are trending.
* Trigger notifications (based on your organization’s [**notification rule**](https://docs.soda.io/soda-v4/manage-issues/notifications)) when anomaly detection flags a group.

### In the **Column Monitoring Configuration API**

{% hint style="info" %}
**Coming soon**
{% endhint %}

***

#### Supported variables

List of all the variables currently supported using `${soda.<variable>}` syntax:

* `SCAN_TIME`: time for which the scan is running; has the same value as `PARTITION_END_TIME` (note this is different from **when** the scan is running)
* `PARTITION_COLUMN`: column used to perform time-based partitioning
* `PARTITION_START_TIME`: start time for the partition time window
* `PARTITION_END_TIME`: end time for the partition time window
* `PARTITION_INTERVAL`: duration of the partition time window
* `TABLE`: qualified name of the table being analyzed, e.g. `"my-schema"."my-table"`

***

{% if visitor.claims.plan === 'free' %}
{% hint style="success" %}
You are **logged in to Soda** and seeing the **Free license** documentation. Learn more about [documentation-access-and-licensing](https://docs.soda.io/soda-v4/reference/documentation-access-and-licensing "mention").
{% endhint %}
{% endif %}

{% if visitor.claims.plan === 'teams' %}
{% hint style="success" %}
You are **logged in to Soda** and seeing the **Team license** documentation. Learn more about [documentation-access-and-licensing](https://docs.soda.io/soda-v4/reference/documentation-access-and-licensing "mention").
{% endhint %}
{% endif %}

{% if visitor.claims.plan === 'enterprise' || visitor.claims.plan === 'enterpriseUserBased' %}
{% hint style="success" %}
You are **logged in to Soda** and seeing the **Enterprise license** documentation. Learn more about [documentation-access-and-licensing](https://docs.soda.io/soda-v4/reference/documentation-access-and-licensing "mention").
{% endhint %}
{% endif %}

{% if !(visitor.claims.plan === 'free' || visitor.claims.plan === 'teams' || visitor.claims.plan === 'enterprise' || visitor.claims.plan === 'enterpriseUserBased') %}
{% hint style="info" %}
You are **not logged in to Soda** and are viewing the default public documentation. Learn more about [documentation-access-and-licensing](https://docs.soda.io/soda-v4/reference/documentation-access-and-licensing "mention").
{% endhint %}
{% endif %}
