Custom SQL Monitor

Run any SQL query on a schedule and alert when the result crosses a threshold.

Overview

Run any SQL query on a schedule and alert when the result crosses a threshold. The custom SQL monitor is your escape hatch when none of the purpose-built types (metric, validation, comparison) can express the check you need.

πŸ“˜

Reference scope

This page covers MaC YAML configuration. For UI-based creation and SQL templates, see Custom SQL Monitors.

Your SQL must return exactly one row with one numeric column. Monte Carlo compares that value to the operator and threshold in your alert_conditions and opens an incident when the condition is met.

Quick Start

montecarlo:
  custom_sql:
    - name: orphan_orders
      description: Alert when orphan orders exist
      warehouse: my-warehouse-name
      sql: |
        SELECT COUNT(*) FROM orders
        WHERE customer_id NOT IN (SELECT id FROM customers)
      schedule:
        type: fixed
        interval_minutes: 720
      alert_conditions:
        - type: threshold
          operator: GT
          threshold_value: 0
      domains:
        - my-domain

Create interactively with create_or_update_sql_monitor(dry_run=True) via the Monte Carlo MCP server.

πŸ“˜

MCP vs MaC naming

The MCP tool parameter is alert_condition (singular object), while MaC YAML uses alert_conditions (plural array). The tool wraps it automatically. The warehouse parameter takes a UUID β€” use get_warehouses to resolve a name.

Configuration

sql β€” SQL query to execute

string Β· required

Must return exactly one row with one numeric column. Avoid trailing semicolons and SQL comments (--, /* */) β€” some warehouses reject them when executed programmatically. Wrap NULLable results with COALESCE.

sql: |
  SELECT COUNT(*) FROM orders
  WHERE customer_id NOT IN (SELECT id FROM customers)
alert_conditions β€” threshold for alerting

array of objects (exactly one entry) Β· required

Exactly one alert condition is required (maxItems: 1). Create separate monitors for multiple thresholds.

Available operators: EQ Β· NEQ Β· GT Β· GTE Β· LT Β· LTE Β· OUTSIDE_RANGE Β· INSIDE_RANGE Β· NOOP Β· AUTO Β· AUTO_HIGH Β· AUTO_LOW

AUTO operators (AUTO, AUTO_HIGH, AUTO_LOW) are only supported with type: dynamic_threshold, not with type: threshold.

Threshold types:

  • Static (threshold) β€” fixed numeric value via threshold_value with an explicit operator.
  • Range β€” lower_threshold and upper_threshold with INSIDE_RANGE or OUTSIDE_RANGE.
  • Dynamic threshold (dynamic_threshold) β€” ML anomaly detection comparing current value against a rolling baseline. Accepts AUTO/AUTO_HIGH/AUTO_LOW, or explicit operators with a threshold relative to the baseline.
  • Change (change) β€” compares the current value against the previous run's value. Requires is_threshold_relative, baseline_agg_function, and baseline_interval_minutes.

Noise reduction: Set event_rollup_count (min 2) or event_rollup_until_changed at the monitor level (not inside alert_conditions) to suppress notifications until N consecutive breaches or until the result changes. These two fields are mutually exclusive.

πŸ“˜

UI name mapping

The UI uses different names for threshold types: Automatic = dynamic_threshold, Absolute = threshold, Relative = change.

alert_conditions:
  - type: threshold
    operator: GT
    threshold_value: 0

Properties


type β€” threshold type

enum Β· optional Β· default: threshold

Accepted values: threshold Β· dynamic_threshold Β· change Β· absolute_volume Β· growth_volume Β· noop

alert_conditions:
  - type: threshold

operator β€” comparison operator

enum Β· required (for threshold and change types)

Accepted values: EQ Β· NEQ Β· GT Β· GTE Β· LT Β· LTE Β· OUTSIDE_RANGE Β· INSIDE_RANGE Β· NOOP Β· AUTO Β· AUTO_HIGH Β· AUTO_LOW

AUTO operators (AUTO, AUTO_HIGH, AUTO_LOW) are only supported with type: dynamic_threshold, not with type: threshold.

alert_conditions:
  - type: threshold
    operator: GT

threshold_value β€” static threshold

number Β· required (for threshold and change types)

alert_conditions:
  - type: threshold
    operator: GT
    threshold_value: 0

lower_threshold β€” lower bound for range operators

number Β· optional

Used with OUTSIDE_RANGE or INSIDE_RANGE operators.

alert_conditions:
  - type: threshold
    operator: OUTSIDE_RANGE
    lower_threshold: 100
    upper_threshold: 1000

upper_threshold β€” upper bound for range operators

number Β· optional

Used with OUTSIDE_RANGE or INSIDE_RANGE operators.

alert_conditions:
  - type: threshold
    operator: INSIDE_RANGE
    lower_threshold: 0
    upper_threshold: 50

baseline_agg_function β€” aggregation for baseline window

enum Β· required (for change and dynamic_threshold types)

Accepted values: AVG Β· MIN Β· MAX

alert_conditions:
  - type: change
    baseline_agg_function: AVG

baseline_interval_minutes β€” lookback window in minutes

integer Β· required (for change and dynamic_threshold types)

Range: 0–129600.

alert_conditions:
  - type: change
    baseline_interval_minutes: 10080

is_threshold_relative β€” percentage vs absolute change

boolean Β· required (when type is change)

Set true for percentage change, false for absolute change.

alert_conditions:
  - type: change
    is_threshold_relative: true

threshold_sensitivity β€” sensitivity for dynamic thresholds

enum Β· optional

Accepted values: low Β· medium Β· high

For dynamic_threshold type only.

alert_conditions:
  - type: dynamic_threshold
    threshold_sensitivity: medium

min_buffer_value β€” minimum buffer for dynamic threshold band

number Β· optional

alert_conditions:
  - type: dynamic_threshold
    min_buffer_value: 10

max_buffer_value β€” maximum buffer for dynamic threshold band

number Β· optional

alert_conditions:
  - type: dynamic_threshold
    max_buffer_value: 100

min_buffer_modifier_type β€” unit for min buffer

enum Β· optional

Accepted values: METRIC Β· PERCENTAGE

alert_conditions:
  - type: dynamic_threshold
    min_buffer_modifier_type: PERCENTAGE

max_buffer_modifier_type β€” unit for max buffer

enum Β· optional

Accepted values: METRIC Β· PERCENTAGE

alert_conditions:
  - type: dynamic_threshold
    max_buffer_modifier_type: PERCENTAGE

number_of_agg_periods β€” aggregation periods in baseline

integer Β· optional

Range: 1–1000.

alert_conditions:
  - type: dynamic_threshold
    number_of_agg_periods: 7

threshold_lookback_minutes β€” lookback window for threshold computation

integer Β· optional

alert_conditions:
  - type: dynamic_threshold
    threshold_lookback_minutes: 10080

is_percentage_threshold β€” percentage-based threshold

boolean Β· optional

alert_conditions:
  - type: threshold
    is_percentage_threshold: true

percentage_baseline_sql β€” SQL for percentage baseline denominator

string Β· optional

alert_conditions:
  - type: threshold
    percentage_baseline_sql: "SELECT COUNT(*) FROM orders"
schedule β€” when and how often to run

object Β· required

Supported modes: fixed, dynamic, manual. Crontab (interval_crontab) is supported. Both the JSON Schema and the CLI require schedule β€” apply aborts if it is omitted.

schedule:
  type: fixed
  interval_minutes: 720

Properties


type β€” schedule type

enum Β· optional Β· default: fixed

Accepted values: fixed Β· dynamic Β· manual

schedule:
  type: fixed

interval_minutes β€” run interval for fixed schedules

integer Β· optional

Required when type is fixed.

schedule:
  type: fixed
  interval_minutes: 720

interval_crontab β€” cron expressions

array of strings Β· optional

5-field cron format.

schedule:
  type: fixed
  interval_crontab:
    - "0 8 * * *"

interval_crontab_day_operator β€” day-of-week/day-of-month combination

enum Β· optional

Accepted values: AND Β· OR

schedule:
  interval_crontab_day_operator: AND

start_time β€” ISO 8601 start time

string Β· optional

schedule:
  start_time: "2024-01-01T08:00:00Z"

timezone β€” IANA timezone

string Β· optional

schedule:
  timezone: America/New_York

dynamic_schedule_tables β€” tables that trigger the monitor

array of strings Β· optional (required when type is dynamic)

Custom SQL monitors support only one table in dynamic_schedule_tables.

schedule:
  type: dynamic
  dynamic_schedule_tables:
    - analytics:public.orders

dynamic_schedule_jobs β€” jobs that trigger the monitor

array of objects Β· optional

PropertyTypeRequiredDescription
job_typeenumyesAdfJob Β· AirflowDag Β· DatabricksJob Β· DbtJob
job_namestringyesName of the job
project_namestringyesProject containing the job
task_namestringnoSpecific task within the job
mconstringnoMCON identifier for the job

schedule:
  type: dynamic
  dynamic_schedule_jobs:
    - job_type: AirflowDag
      job_name: etl_orders
      project_name: my-airflow

min_interval_minutes β€” minimum interval for dynamic schedules

integer Β· optional

schedule:
  type: dynamic
  min_interval_minutes: 30
description β€” what this monitor checks

string Β· required

Displayed in the Monte Carlo UI and in incident notifications. Max 512 characters.

description: Alert when orphan orders exist
domains β€” domain for this monitor

array of strings (exactly one entry) Β· required, on all accounts created after January 2025

Set default_domain in montecarlo.yml to avoid repeating it on every monitor.

domains:
  - my-domain
warehouse β€” which warehouse to use

string Β· optional (yes if multiple warehouses)

Warehouse name or UUID. Overrides default_resource from montecarlo.yml.

warehouse: my-warehouse
name β€” unique identifier within the namespace

string Β· required

Required for monitors created after Jan 29, 2024 (existing monitors keep working). Changing the name creates a new monitor and deletes the old one β€” incident history does not transfer.

name: orphan_orders
connection_name β€” named connection

string Β· optional

Overrides the default connection for this warehouse.

connection_name: analytics-readonly
variables β€” template variables substituted into SQL

object Β· optional Β· default: {}

Variables are substituted into the sql field using {{ variable_name }} syntax. When variables are defined, Monte Carlo runs the SQL once per combination of variable values and evaluates each result independently. Two formats are supported: static (list of values) and runtime (object with type: runtime and default).

variables:
  schema_name:
    - analytics
    - staging
sampling_sql β€” investigation query on breach

string Β· optional

SQL returning sample rows on breach for incident context. Called "investigation query" in the UI. Runs only on breach β€” zero load on clean runs.

sampling_sql: |
  SELECT * FROM orders
  WHERE customer_id NOT IN (SELECT id FROM customers)
  LIMIT 100
query_result_type β€” how to interpret the SQL result

enum Β· optional

Accepted values: ROW_COUNT Β· SINGLE_NUMERIC

ROW_COUNT evaluates the row count of the result set instead of the scalar value.

query_result_type: ROW_COUNT
event_rollup_count β€” consecutive breaches before alerting

integer Β· optional

Minimum 2. Mutually exclusive with event_rollup_until_changed. Rollup caps at 100 consecutive breaches.

event_rollup_count: 3
event_rollup_until_changed β€” suppress repeat notifications

boolean Β· optional Β· default: false

Roll up breaches until the result changes. Mutually exclusive with event_rollup_count.

event_rollup_until_changed: true
severity β€” incident severity level

enum Β· optional

Accepted values: SEV-0 Β· SEV-1 Β· SEV-2 Β· SEV-3 Β· SEV-4

severity: SEV-2
priority β€” incident priority level

enum Β· optional

Accepted values: P1 Β· P2 Β· P3 Β· P4 Β· P5

priority: P2
audiences β€” notification channels

array of strings Β· optional Β· default: []

Audience names linking this monitor to channels defined in Notifications as Code. In exported/rendered YAML, appears as labels.

audiences:
  - data-eng-oncall
  - platform-alerts
failure_audiences β€” notification channels for run failures

array of strings Β· optional

Separate audiences for run-failure notifications. Falls back to audiences when omitted.

failure_audiences:
  - data-eng-oncall
notify_run_failure β€” notify when the query itself fails

boolean Β· optional

notify_run_failure: true
exception_primary_key_column β€” column for per-key incident grouping

string Β· optional

exception_primary_key_column: order_id
timeout β€” query timeout in seconds

integer Β· optional

timeout: 300
tags β€” key-value pairs for organizing monitors

array of objects Β· optional Β· default: []

PropertyTypeRequiredDescription
namestringyesTag key
valuestringnoTag value

tags:
  - name: team
    value: analytics
  - name: environment
    value: production
data_quality_dimension β€” data quality category

enum Β· optional

Accepted values: ACCURACY Β· COMPLETENESS Β· CONSISTENCY Β· TIMELINESS Β· UNIQUENESS Β· VALIDITY

data_quality_dimension: CONSISTENCY
notes β€” internal notes

string Β· optional

Visible in the Monte Carlo UI. Not included in notifications.

notes: Owned by the analytics team. Reviewed quarterly.
is_draft β€” create as draft without activating

boolean Β· optional Β· default: false

Creates the monitor in a paused state. Omitting this on a later update resets to false (active) due to PUT semantics β€” always include it if you want the monitor to stay in draft.

is_draft: true
uuid β€” update an existing monitor

string Β· optional

Include the UUID of an existing monitor to update it instead of creating a new one.

uuid: 0dae7702-0950-45c7-909c-7e183bddca19
Deprecated fields
FieldUse instead
resourcewarehouse
domaindomains
domain_uuidsdomains
labelsaudiences
notify_rule_run_failurenotify_run_failure
comparisonsalert_conditions
πŸ“˜

API-only fields

Some fields visible in the API or JSON Schema (skip_reset, fail_on_reset, metadata) are present in the schema but silently stripped during MaC YAML processing. They have no effect and should not be included.

Examples

Threshold check β€” alert when orphan rows exist

montecarlo:
  custom_sql:
    - name: orphan_orders
      description: Detect orders referencing deleted customers
      warehouse: my-warehouse
      sql: |
        SELECT COUNT(*) FROM orders
        WHERE customer_id NOT IN (SELECT id FROM customers)
      schedule:
        type: fixed
        interval_minutes: 720
      alert_conditions:
        - type: threshold
          operator: GT
          threshold_value: 0
      priority: P2
      data_quality_dimension: CONSISTENCY
      audiences:
        - data-eng-oncall
      domains:
        - my-domain

Change detection β€” alert when metric changes more than 20% from baseline

montecarlo:
  custom_sql:
    - name: revenue_change
      description: Alert on significant daily revenue change
      warehouse: my-warehouse
      sql: |
        SELECT COALESCE(SUM(amount), 0)
        FROM transactions
        WHERE created_at >= CURRENT_DATE - 1
          AND created_at < CURRENT_DATE
      schedule:
        type: fixed
        interval_minutes: 1440
      alert_conditions:
        - type: change
          operator: GT
          threshold_value: 0.2
          is_threshold_relative: true
          baseline_agg_function: AVG
          baseline_interval_minutes: 10080
      domains:
        - my-domain

Anomaly detection β€” alert when result deviates from learned baseline

montecarlo:
  custom_sql:
    - name: daily_revenue_anomaly
      description: Detect anomalous daily revenue using ML
      warehouse: my-warehouse
      sql: |
        SELECT COALESCE(SUM(amount), 0)
        FROM transactions
        WHERE created_at >= CURRENT_DATE - 1
          AND created_at < CURRENT_DATE
      schedule:
        type: fixed
        interval_minutes: 1440
      alert_conditions:
        - type: dynamic_threshold
          operator: AUTO
          baseline_agg_function: AVG
          baseline_interval_minutes: 10080
          threshold_sensitivity: medium
      domains:
        - my-domain

Troubleshooting

Alert conditions and thresholds

  • AUTO operators not supported for threshold type. AUTO operators (AUTO, AUTO_HIGH, AUTO_LOW) require type: dynamic_threshold. If your alert condition uses type: threshold (the default), switch to type: dynamic_threshold to use anomaly detection, or use an explicit operator (GT, LT, etc.).
  • Incomplete change conditions. type: change requires all four: baseline_agg_function, baseline_interval_minutes, is_threshold_relative, and threshold_value. Omitting is_threshold_relative produces: "is_threshold_relative is a required field."
  • Multiple alert conditions. Exactly one is required. Create separate monitors for multiple thresholds.

SQL pitfalls

  • SQL returns NULL. A NULL silently passes. Wrap with COALESCE: SELECT COALESCE(SUM(amount), 0) ...
  • Trailing semicolons or comments. Avoid trailing semicolons and SQL comments (--, /* */) β€” some warehouses reject them when executed programmatically.

Noise reduction and updates

  • event_rollup_count and event_rollup_until_changed are mutually exclusive. Setting both causes a validation error. Choose one strategy.
  • Forgetting PUT semantics on updates. When providing uuid, omitted fields revert to defaults. Always include every field you want to preserve.