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 scopeThis 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-domainCreate interactively with create_or_update_sql_monitor(dry_run=True) via the Monte Carlo MCP server.
MCP vs MaC namingThe MCP tool parameter is
alert_condition(singular object), while MaC YAML usesalert_conditions(plural array). The tool wraps it automatically. Thewarehouseparameter takes a UUID β useget_warehousesto resolve a name.
Configuration
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)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 viathreshold_valuewith an explicit operator. - Range β
lower_thresholdandupper_thresholdwithINSIDE_RANGEorOUTSIDE_RANGE. - Dynamic threshold (
dynamic_threshold) β ML anomaly detection comparing current value against a rolling baseline. AcceptsAUTO/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. Requiresis_threshold_relative,baseline_agg_function, andbaseline_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 mappingThe UI uses different names for threshold types: Automatic =
dynamic_threshold, Absolute =threshold, Relative =change.
alert_conditions:
- type: threshold
operator: GT
threshold_value: 0Properties
type β threshold type
enum Β· optional Β· default: threshold
Accepted values: threshold Β· dynamic_threshold Β· change Β· absolute_volume Β· growth_volume Β· noop
alert_conditions:
- type: thresholdoperator β 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: GTthreshold_value β static threshold
number Β· required (for threshold and change types)
alert_conditions:
- type: threshold
operator: GT
threshold_value: 0lower_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: 1000upper_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: 50baseline_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: AVGbaseline_interval_minutes β lookback window in minutes
integer Β· required (for change and dynamic_threshold types)
Range: 0β129600.
alert_conditions:
- type: change
baseline_interval_minutes: 10080is_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: truethreshold_sensitivity β sensitivity for dynamic thresholds
enum Β· optional
Accepted values: low Β· medium Β· high
For dynamic_threshold type only.
alert_conditions:
- type: dynamic_threshold
threshold_sensitivity: mediummin_buffer_value β minimum buffer for dynamic threshold band
number Β· optional
alert_conditions:
- type: dynamic_threshold
min_buffer_value: 10max_buffer_value β maximum buffer for dynamic threshold band
number Β· optional
alert_conditions:
- type: dynamic_threshold
max_buffer_value: 100min_buffer_modifier_type β unit for min buffer
enum Β· optional
Accepted values: METRIC Β· PERCENTAGE
alert_conditions:
- type: dynamic_threshold
min_buffer_modifier_type: PERCENTAGEmax_buffer_modifier_type β unit for max buffer
enum Β· optional
Accepted values: METRIC Β· PERCENTAGE
alert_conditions:
- type: dynamic_threshold
max_buffer_modifier_type: PERCENTAGEnumber_of_agg_periods β aggregation periods in baseline
integer Β· optional
Range: 1β1000.
alert_conditions:
- type: dynamic_threshold
number_of_agg_periods: 7threshold_lookback_minutes β lookback window for threshold computation
integer Β· optional
alert_conditions:
- type: dynamic_threshold
threshold_lookback_minutes: 10080is_percentage_threshold β percentage-based threshold
boolean Β· optional
alert_conditions:
- type: threshold
is_percentage_threshold: truepercentage_baseline_sql β SQL for percentage baseline denominator
string Β· optional
alert_conditions:
- type: threshold
percentage_baseline_sql: "SELECT COUNT(*) FROM orders"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: 720Properties
type β schedule type
enum Β· optional Β· default: fixed
Accepted values: fixed Β· dynamic Β· manual
schedule:
type: fixedinterval_minutes β run interval for fixed schedules
integer Β· optional
Required when type is fixed.
schedule:
type: fixed
interval_minutes: 720interval_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: ANDstart_time β ISO 8601 start time
string Β· optional
schedule:
start_time: "2024-01-01T08:00:00Z"timezone β IANA timezone
string Β· optional
schedule:
timezone: America/New_Yorkdynamic_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.ordersdynamic_schedule_jobs β jobs that trigger the monitor
array of objects Β· optional
| Property | Type | Required | Description |
|---|---|---|---|
job_type | enum | yes | AdfJob Β· AirflowDag Β· DatabricksJob Β· DbtJob |
job_name | string | yes | Name of the job |
project_name | string | yes | Project containing the job |
task_name | string | no | Specific task within the job |
mcon | string | no | MCON identifier for the job |
schedule:
type: dynamic
dynamic_schedule_jobs:
- job_type: AirflowDag
job_name: etl_orders
project_name: my-airflowmin_interval_minutes β minimum interval for dynamic schedules
integer Β· optional
schedule:
type: dynamic
min_interval_minutes: 30string Β· required
Displayed in the Monte Carlo UI and in incident notifications. Max 512 characters.
description: Alert when orphan orders existarray 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-domainstring Β· optional (yes if multiple warehouses)
Warehouse name or UUID. Overrides default_resource from montecarlo.yml.
warehouse: my-warehousestring Β· 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_ordersstring Β· optional
Overrides the default connection for this warehouse.
connection_name: analytics-readonlyobject Β· 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
- stagingstring Β· 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 100enum Β· 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_COUNTinteger Β· optional
Minimum 2. Mutually exclusive with event_rollup_until_changed. Rollup caps at 100 consecutive breaches.
event_rollup_count: 3boolean Β· optional Β· default: false
Roll up breaches until the result changes. Mutually exclusive with event_rollup_count.
event_rollup_until_changed: trueenum Β· optional
Accepted values: SEV-0 Β· SEV-1 Β· SEV-2 Β· SEV-3 Β· SEV-4
severity: SEV-2enum Β· optional
Accepted values: P1 Β· P2 Β· P3 Β· P4 Β· P5
priority: P2array 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-alertsarray of strings Β· optional
Separate audiences for run-failure notifications. Falls back to audiences when omitted.
failure_audiences:
- data-eng-oncallboolean Β· optional
notify_run_failure: truestring Β· optional
exception_primary_key_column: order_idinteger Β· optional
timeout: 300array of objects Β· optional Β· default: []
| Property | Type | Required | Description |
|---|---|---|---|
name | string | yes | Tag key |
value | string | no | Tag value |
tags:
- name: team
value: analytics
- name: environment
value: productionenum Β· optional
Accepted values: ACCURACY Β· COMPLETENESS Β· CONSISTENCY Β· TIMELINESS Β· UNIQUENESS Β· VALIDITY
data_quality_dimension: CONSISTENCYstring Β· optional
Visible in the Monte Carlo UI. Not included in notifications.
notes: Owned by the analytics team. Reviewed quarterly.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: truestring Β· optional
Include the UUID of an existing monitor to update it instead of creating a new one.
uuid: 0dae7702-0950-45c7-909c-7e183bddca19Deprecated fields
| Field | Use instead |
|---|---|
resource | warehouse |
domain | domains |
domain_uuids | domains |
labels | audiences |
notify_rule_run_failure | notify_run_failure |
comparisons | alert_conditions |
API-only fieldsSome 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-domainChange 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-domainAnomaly 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-domainTroubleshooting
Alert conditions and thresholds
- AUTO operators not supported for
thresholdtype. AUTO operators (AUTO,AUTO_HIGH,AUTO_LOW) requiretype: dynamic_threshold. If your alert condition usestype: threshold(the default), switch totype: dynamic_thresholdto use anomaly detection, or use an explicit operator (GT, LT, etc.). - Incomplete
changeconditions.type: changerequires all four:baseline_agg_function,baseline_interval_minutes,is_threshold_relative, andthreshold_value. Omittingis_threshold_relativeproduces: "is_threshold_relativeis a required field." - Multiple alert conditions. Exactly one is required. Create separate monitors for multiple thresholds.
SQL pitfalls
- SQL returns NULL. A
NULLsilently passes. Wrap withCOALESCE: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_countandevent_rollup_until_changedare 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.
Updated about 2 hours ago
