Deployment Rules define where and how monitors apply across your data stack. Instead of manually adding individual monitors, you can set up rules that dynamically apply the right monitors based on your data source, Data Product, or specific table groups.Key Benefits:
Faster setup – Apply monitoring in bulk instead of configuring each asset manually
Better control – Adjust sensitivity and thresholds across multiple monitors at once
More flexibility – Define and manage exclusions and overrides
A deployment rule pairs a selection with one of two payloads:
table_stats — the monitors and metrics covered above, deployed onto every asset the selection matches.
sql_tests — the SQL tests listed under the rule’s own tests:, deployed onto every table or view the selection matches.
Both kinds live in the same deployment_rules: list of a monitors-as-code configuration:
version: v1beta2deployment_rules: - name: PII email checks type: sql_tests resolver_ql: with_columns("email") schedule: daily severity: ERROR keep_removed_tests: false tests: - type: not_null columns: [email] - type: business_rule name: email looks like an address sql_expression: email NOT LIKE '%@%'
Every field, with its type and validation rules, is in the configuration reference; the commands that deploy a configuration are in the CLI reference. The rest of this section is what a sql_tests rule does once it is deployed.
A test is deployed onto a matched table only if that table has every column the test reads — the columns it checks, any select_columns, a unique or relationships test’s time_partition_column, and for relationships the columns on the referenced table as well. A table missing one of them shows up in the deploy plan as a skipped test and gets no test at all: such a test could only fail every run.That makes a broad selection safe to write. A rule carrying three tests covers each matched table with the ones that fit it.A test is skipped whole, never narrowed. A not_null on [email, account_id] against a table that has only email is not reduced to email alone, because unique(a, b) is a different assertion from unique(a). Declare one test per column when you want that granularity.A business_rule is gated on its select_columns alone: its SQL expression is never parsed, so a rule whose expression reads status without declaring it deploys anyway, while one that declares select_columns: [status] is skipped on every table without that column. A business_query declares no columns of its own and is never gated.A matched table whose schema has not been ingested keeps the tests it already carries and is retried on the next sync; a test not yet deployed there waits rather than being created, since whether the columns are present cannot be answered either way.
The tests a rule deployed onto a table are deleted once that table stops matching. Set keep_removed_tests: true to leave them in place instead. The rule then stops resyncing those tests altogether, and they are yours to remove if they start failing.A column dropped from a table the rule still matches is a different case: that table’s test goes away on the next sync, and keep_removed_tests does not protect it. The flag is about the table leaving the selection, not about a test losing what it reads.
Editing a rule’s selection resyncs it. Tests appear on the tables the edited selection matches, go away from the tables it stops matching, and tables matched both before and after keep the tests they already had.Deleting a sql_tests rule deletes every test the rule deployed, together with those tests’ check entities and their history. Nothing brings them back.A rule is addressed by its id: when it carries one, and by its name within its namespace when it does not — so renaming a rule that has no id: replaces it: the old rule is deleted with everything it deployed, and the next scheduled sync builds the tests again from scratch under the new name. Give a rule an id: before you need to rename it, and renaming is an update instead.
A relationships test under a rule names one reference table per references[] entry, and every matched table is checked against that same table — many tables carrying customer_id against one dim.customers. The reference is not resolved per match, so a rule cannot express “each staging.X against its own raw.X”. Write those tests one per table, under the entity they belong to.The reference table has to sit in the same integration as the matched tables, because the test runs on the matched table’s connection. Nothing narrows the selection to that integration for you, so confine it yourself with with_integration_ids(...). A match in another integration gets a test whose reference resolves on the wrong connection: it fails every run, or it joins a table that happens to share the name.A referenced table that has not been ingested leaves the test waiting indefinitely, and the deploy plan says so, naming the table.
See Monitors overview for best practices when setting up monitors. Specifically for deployment rules, we recommend you consider:
Use exclusions to reduce noise - remove temporary tables, or datasets where fluctuations are expected.
Override sensitivity - for example, for financial data configure tighter alert thresholds to detect even small anomalies. For less important data set a looser threshold to only get alerted on significant deviations.
A rule’s selection, metrics, sensitivity and exclusions can be read, previewed and
deployed outside the app.
Public API — synq.monitors.automated_monitors.v1.DeploymentRulesService.
Every method on it, reads included, needs a token with
SCOPE_MONITORS_AUTOMATIC_EDIT. See the API reference and API scopes.
MCP — the deployment rule tools let an AI
assistant author a selection, see exactly which monitors a rule would create,
keep and delete before anything happens, and deploy it once you approve.
Previewing is side-effect-free and doubles as validation of the selection
query; deploying and deleting are confirmation-gated and need the Deploy
permission.