> For the complete documentation index, see [llms.txt](https://docs.soda.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.soda.io/soda-documentation/data-testing/cloud-managed-data-contracts/contract-scopes.md).

# Contract scopes

Group the checks of one contract into scopes, each with its own filter, schedule and check attributes, and review, verify and get notified per scope in Soda Cloud.

{% hint style="info" %}
**Preview feature.** Contract scopes are in [**private preview**](/soda-documentation/reference/documentation-access-and-licensing/release-stages.md#private-preview).

Contact us at <support@soda.io> to enable them in your organization.
{% endhint %}

A **contract scope** is a named group of checks inside a dataset's contract. Each scope can have its own filter, schedule and check attributes, and Soda Cloud keeps a separate result history for the checks of each scope.

Use scopes when different teams or processes need to own, schedule or run different groups of checks on the same dataset. For example:

* A finance team checks only the orders of the current month, every weekday morning.
* An operations team runs frequent checks every 30 minutes.
* A release pipeline runs a set of critical checks on demand before it deploys a change.

All of these live in **one contract file**, with one editor, one version history and one publish flow. A scope is not a second contract, and Soda Cloud has no separate scope object: the contract file is the only place where scopes are defined.

## How scopes work

* Declare scopes under the top-level `scopes` key of the contract. Each scope has a fixed **key**, such as `finance`, and a display **name**.
* Assign a check to a scope with `scope: <key>` in the check. A check belongs to at most one scope.
* A check without `scope` belongs to no scope. Soda Cloud shows these checks as **Without scope**. They run and behave exactly as in a contract without scopes.
* A scope **does not inherit** the contract's top-level `filter`, schedule or `check_attributes`. It uses only what it declares itself.
* If you need the same check in two scopes, add it twice, once per scope. Each copy is a separate check with its own history.

{% code title="contract.yml" %}

```yaml
dataset: snowflake/analytics/public/orders

filter: is_deleted = false

soda_runner:
  checks_schedule:
    cron: "0 6 * * *"
    timezone: UTC

check_attributes:
  domain: commerce

scopes:
  finance:
    name: Finance
    description: Orders in the current accounting month
    filter: |
      is_deleted = false
      AND order_date >= DATE_TRUNC('month', CURRENT_DATE)
    schedule:
      cron: "0 7 * * 1-5"
      timezone: Europe/Brussels
    check_attributes:
      domain: finance

  release_gate:
    name: Release gate
    description: Critical checks run before a pipeline change is deployed

checks:
  - row_count:
  - row_count:
      scope: release_gate

columns:
  - name: order_id
    checks:
      - missing:
      - missing:
          scope: release_gate
  - name: amount
    checks:
      - invalid:
          scope: finance
          valid_min: 0
  - name: order_date
  - name: is_deleted
```

{% endcode %}

In this contract:

* The first `row_count` and `missing` checks have no scope. They read the rows where `is_deleted = false`, run every day at 06:00 UTC, and carry the attribute `domain: commerce`.
* The `invalid` check on `amount` belongs to **Finance**. It reads only the orders of the current month, runs on weekdays at 07:00 Brussels time, and carries `domain: finance`.
* The checks with `scope: release_gate` belong to **Release gate**. This scope has no filter, so its checks read every row. It has no schedule, so its checks run only when you verify the scope manually or programmatically.

> For every scope key, the key rules, how filters and attributes apply to scoped checks, and how to run a scope from the Soda CLI, see [Contract Language reference](/soda-documentation/reference/contract-language-reference.md#contract-scopes).

### Schedules

A scope runs in one of two ways:

| Scope schedule                   | How the scope runs                                                                                                               |
| -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| No `schedule` (the default)      | **Manual or programmatic.** The checks of the scope run only when you verify the scope from Soda Cloud, the Soda CLI or the API. |
| A `schedule` with its own `cron` | Soda Cloud runs the checks of the scope on that cron, in that time zone, with the variable values set in the schedule.           |

Once a contract declares at least one scope, the contract's own schedule (`soda_runner.checks_schedule`) runs **only the checks without a scope**. It never runs the checks of a scope.

When you publish the contract, Soda Cloud creates a scan definition for every scope. A scope with a schedule runs on its own cron, and its scheduled scans run only the checks of that scope. A scope without a schedule has no cron. The scan definition of a scope is named `scope.<key>:<dataset fully qualified name>`, for example `scope.finance:snowflake/analytics/public/orders`.

Scope schedules follow the same rules as the contract schedule:

* The cron must not run more often than every 15 minutes.
* The data source must be configured in Soda Cloud, with a Soda Runner that can run contracts. To run only the checks of a scope, the runner must be `Soda Runner 2.0.18` or later (equivalent to `Soda Agent 1.4.32`); see [#verify-a-scope](#verify-a-scope "mention").
* Without a `timezone`, the schedule runs in UTC.

### Check identity and history

The scope is part of a check's identity. As a result:

* The same check in two scopes is two checks in Soda Cloud, each with its own result history, status and notifications.
* **Moving a check** into a scope, out of a scope or to another scope creates a **new check** with an empty history. The history stays with the old check, which is archived after the next contract verification.
* **Removing a scope** from the contract archives its checks after the next contract verification. It also deletes the scope's scan definition, together with the scans that ran on it.
* A scope key cannot be renamed. Changing a key in the YAML removes the old scope and adds a new one, with new checks and new histories. You can change a scope's **name** and **description** at any time without affecting its checks.
* The same check can appear once outside any scope and once in each scope, with no `qualifier`. Two checks of the same type in the **same** scope need distinct qualifiers, as they do outside a scope. See [Contract Language reference](/soda-documentation/reference/contract-language-reference.md#check-qualifiers).

## Add and edit scopes in the contract editor

You add, edit and remove scopes in the [Contract Editor](/soda-documentation/data-testing/cloud-managed-data-contracts/author-a-contract-in-soda-cloud.md), in the same contract as all other checks.

{% hint style="warning" %}
This action requires the **Manage contract** permission on the dataset. Learn more about permissions here: [Dataset Attributes & Responsibilities](/soda-documentation/dataset-attributes-and-responsibilities.md)
{% endhint %}

### Add a scope

{% stepper %}
{% step %}
On the dataset page, click **Edit Contract** to open the Contract Editor.
{% endstep %}

{% step %}
In the **Scopes** section, below **General**, click **+ Add scope**. Once the contract has scopes, you can also click **+** (**Add scope**) in the **Scopes** section header or next to the scope pills at the top of the editor.

<figure><img src="https://1123167021-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FA2PmHkO5cBgeRPdiPPOG%2Fuploads%2Fgit-blob-e3ef6f78c3225cb3cd69587753ec69a03b226dc6%2Fcontract-scopes-editor-scopes-section.png?alt=media" alt="Contract Editor with the General section and, below it, an empty Scopes section showing No scopes defined, the Add scope button and a plus icon in the section header"><figcaption></figcaption></figure>
{% endstep %}

{% step %}
In the **New scope** dialog, fill in the scope:

* **Name** (required): the display name of the scope, such as `EU orders`.
* **ID**: the scope key, which identifies the scope in the contract YAML and on the CLI (`scope=<ID>`). Soda generates it from the name; click **Edit** to change it before you add the scope. After that, the ID cannot change.
* **Description** (optional): what the scope covers.
* **Filter** (optional): a SQL filter, without the `WHERE` keyword, such as `region = 'EU'`. Only rows matching this filter are checked. The contract filter does not apply to a scope, so repeat any condition of the contract filter that the scope needs. Without a filter, the checks of the scope read every row.
* **Schedule**:
  * **Manual or programmatic** (default): the scope runs only when you start it, from Soda Cloud, the CLI or the API. The contract schedule does not run this scope.
  * **Custom schedule**: a cron expression and a time zone. A new custom schedule starts from the contract's schedule. Under **Variables override**, click **+ Add variable override** to give a contract variable another value for the scheduled runs of this scope.
* **Check attributes** (optional): the default attributes for the checks of the scope. The contract's check attributes do not apply to the checks of a scope.

<figure><img src="https://1123167021-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FA2PmHkO5cBgeRPdiPPOG%2Fuploads%2Fgit-blob-32782c22bdaa80645c7c24bc85a0a7cf6edab69e%2Fcontract-scopes-new-scope-dialog.png?alt=media" alt="New scope dialog with the name Finance, the generated ID finance, a description, a filter, and Custom schedule selected" width="563"><figcaption></figcaption></figure>

The ID must start with a lowercase letter, followed by lowercase letters, digits, `-` or `_`, with at most 64 characters in total. It must be unique in the contract, and it cannot be `base` or one of the words YAML reads as a boolean or null: `true`, `false`, `null`, `yes`, `no`, `on`, `off`, `y`, `n`.
{% endstep %}

{% step %}
Click **Add scope**. The scope appears in the **Scopes** section.
{% endstep %}

{% step %}
Add checks to the scope, then **Publish** the contract. See [#assign-checks-to-a-scope](#assign-checks-to-a-scope "mention").
{% endstep %}
{% endstepper %}

### Edit or remove a scope

Each scope in the **Scopes** section shows its name and its **ID**. Expand a scope to edit its name, description, filter, schedule and check attributes inline. The ID cannot be edited.

<figure><img src="https://1123167021-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FA2PmHkO5cBgeRPdiPPOG%2Fuploads%2Fgit-blob-31cec5b8dd5e3e0234bd505138af1ae4201e89c6%2Fcontract-scopes-editor-scope-entry.png?alt=media" alt="Expanded Finance scope in the Scopes section with its name, ID, description, filter, a custom weekday schedule in Brussels time, and the Variables override and Attributes sections" width="563"><figcaption></figcaption></figure>

To remove a scope, click the **Remove scope** (trash) icon in its header.

* A scope that was never published is deleted right away.
* A published scope and all of its checks stay in the editor, tagged **Removed** and read-only, until you publish. Click **Revert scope** to restore the scope together with its checks. Revert is not available while another scope uses the same ID.

When you publish, the scope and its checks are removed from the contract. See [#check-identity-and-history](#check-identity-and-history "mention") for what happens to their results.

### Filter the editor by scope

When the contract has at least one scope, a row of pills appears at the top of the editor:

* **Full contract** shows every check in the contract.
* **Without scope** shows only the checks without a scope.
* A **scope pill** shows only the checks of that scope.

The pills filter the dataset-level, column-level and reconciliation check sections. Checks stay in their usual place in the contract: a scope does not repeat columns or their schema details.

<figure><img src="https://1123167021-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FA2PmHkO5cBgeRPdiPPOG%2Fuploads%2Fgit-blob-48651b9899c09c1a6a32cbcd855b47291d1af2a3%2Fcontract-scopes-editor-pills.png?alt=media" alt="Top of the Contract Editor with the Full contract, Without scope and Finance pills, each with a check count, and the Add scope plus icon"><figcaption></figcaption></figure>

When you open the editor with **Edit Contract** from a scope or from **Without scope** on the Checks tab, the editor opens with that pill selected.

### Assign checks to a scope

Every check form has a **Scope** select, with **Without scope** and every scope of the contract. This includes dataset-level, column-level and reconciliation checks, and the schema check.

* A check you add while a scope pill is selected starts in that scope.
* A check you add on **Full contract** or **Without scope** starts without a scope.
* Leave the select on **Without scope** to keep a check out of every scope.

A collapsed check shows its scope as a tag. If a check refers to a scope that the contract does not declare, for example after a typo in the code view, the select warns **This scope is not declared in the contract**.

<figure><img src="https://1123167021-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FA2PmHkO5cBgeRPdiPPOG%2Fuploads%2Fgit-blob-af7a0caf55cfdf54683886c8972b882b1f2763a2%2Fcontract-scopes-check-scope-select.png?alt=media" alt="Missing check form with the Scope select open on Without scope and Finance, and a Finance tag next to the check name" width="563"><figcaption></figcaption></figure>

{% hint style="warning" %}
Changing the scope of a **published** check makes it a new check, as the select's helper text says: *"This check is published. A new scope makes it a new check; its result history stays with this one."* The published check stays in the editor, marked as removed, with a **Revert** action. Moving the check back to its original scope restores the published check.
{% endhint %}

In the code view, set `scope: <key>` inside the check:

```yaml
columns:
  - name: amount
    checks:
      - invalid:
          scope: finance
          valid_min: 0
```

### Publish a contract with scopes

Soda Cloud validates the whole contract when you publish it, scopes included. The contract is published as one file: if a scope or a scope reference is invalid, **the whole contract is rejected** and nothing is published. For example:

* A check refers to a scope the contract does not declare:\
  `Scope us is not declared under 'scopes'; used by: scope.us:checks.row_count`
* Two checks of the same type in one scope have no distinct qualifier:\
  `Check path scope.eu:columns.amount.checks.missing is used by 2 checks; give each a distinct qualifier`
* A scope schedule is invalid, such as a cron that runs more often than every 15 minutes:\
  `Scope 'nightly': Cron expression must be at least 15 minutes apart`

## Review checks by scope on the dataset page

On the dataset's **Checks** tab, a row of pills lets you look at the checks of one part of the contract at a time:

| Pill               | Shows                                                                                              |
| ------------------ | -------------------------------------------------------------------------------------------------- |
| **All checks**     | Every check on the dataset: the checks without a scope, every scope and the linked Data Standards. |
| **Without scope**  | The checks without a scope.                                                                        |
| A **scope** pill   | The checks of that scope only. Scope pills appear in the order of the contract file.               |
| **Data Standards** | The checks that come from Data Standards.                                                          |

Each pill shows a status dot and its number of checks. Hover over a scope pill to see its schedule, its filter and the outcomes of its checks. When the pills don't fit on one line, the remaining ones are grouped under **+N more**. The pill row shows once the dataset has at least two of these groups besides **All checks**.

<figure><img src="https://1123167021-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FA2PmHkO5cBgeRPdiPPOG%2Fuploads%2Fgit-blob-20fd04d0196aacb8fa72be373d0ce36acfd0c7ec%2Fcontract-scopes-checks-tab-all-checks.png?alt=media" alt="Checks tab on All checks with Without scope and Finance origin labels on the check rows, a Scope: Finance (finance) tooltip, and the Scopes list in the right column"><figcaption></figcaption></figure>

Selecting a pill limits the check tree, the summary and the **Verify** action to that pill. Selecting a pill also clears the other filters on the tab.

### Origin of each check

On **All checks**, each check row and column row shows where its checks come from:

* **Without scope**, with a document icon.
* The **scope name**, with a funnel icon.
* The **Data Standard name**, with a shield icon.

Click a **Without scope** or scope label to select that pill. Click a Data Standard label to open the standard.

### Scope details

The right column describes what the selected pill covers:

* On **All checks**, the **Scopes** list shows each scope with its schedule and number of checks. Click a scope to select its pill.
* On a **scope** pill, the scope card shows the scope's name, its **ID**, its description, its **Filter**, its **Schedule**, its **Attributes** and its **Last run**. A scope without a filter shows **Every row, no filter**. A scope without a schedule shows **Manual or programmatic**; a custom schedule shows in words, such as *At 06:00 AM (Europe/Brussels)*.
* On **Without scope**, the card shows the contract filter, the contract schedule and the last run of the checks without a scope.

<figure><img src="https://1123167021-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FA2PmHkO5cBgeRPdiPPOG%2Fuploads%2Fgit-blob-4c3827d1e33ee72b77040b0e7f5f3497901f9870%2Fcontract-scopes-checks-tab-scope-card.png?alt=media" alt="Checks tab with the Finance pill selected, its single check, the Verify scope button, and the scope card with ID, description, filter, schedule, attributes and last run"><figcaption></figcaption></figure>

### Filter checks by scope

The filter pane of the Checks tab has a **Scope** filter when the contract declares at least one scope. Select **Without scope** and one or more scopes to narrow the checks further, for example on **All checks**. The Scope filter applies to contract checks only: Data Standard checks never match it.

When two scopes have the same name, the filter shows their key in parentheses, such as **EU (eu\_orders)**.

### Saved views

The selected pill is part of the page URL, so you can share a link to a scope, and a saved view restores the scope it was saved with.

A view you save on the Checks tab belongs to the dataset you saved it on. Once the dataset has such views, the view list groups them in sections:

* **This dataset**: views saved on this dataset.
* **All datasets**: views available on every dataset.
* **Built-in**: the default views.

As the owner of a view, use **Make available on all datasets** to offer it on every dataset, or **Limit to this dataset** to bind it to the dataset you are on. If the view filters by a scope, Soda warns that it shows no checks on datasets without that scope.

If a saved view or a link refers to a scope that the contract no longer declares, its pill shows a warning icon with the tooltip **This scope has been removed**, and the tab shows no active checks for it.

## Verify a scope

You can verify a scope from the Checks tab whether or not it has a schedule. The **Verify** button follows the selected pill:

| Pill               | Button                          | Runs                                     |
| ------------------ | ------------------------------- | ---------------------------------------- |
| **All checks**     | **Verify all checks**           | Every check on the dataset.              |
| **Without scope**  | **Verify checks without scope** | The checks without a scope.              |
| A **scope** pill   | **Verify scope**                | The checks of that scope only.           |
| **Data Standards** | **Verify data standard**        | The checks of the linked Data Standards. |

When you have filtered the checks of the **Without scope** pill or a scope pill, **Verify with applied filters** runs only the matching checks of that pill. If every contract variable has a default value, the menu also offers to verify with other variable values, such as **Verify scope with variables**. A manual verification uses the contract's variable values, not the **Variables override** values of a scope schedule.

If the selected scope has no checks to run, for example because it was removed from the contract, the button shows **No checks to verify**.

<figure><img src="https://1123167021-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FA2PmHkO5cBgeRPdiPPOG%2Fuploads%2Fgit-blob-1d2b134d5dc0c7252871240f9d993d4820908bdd%2Fcontract-scopes-verify-scope.png?alt=media" alt="Verify scope button with its menu open on Verify scope with variables, with the Finance pill selected"><figcaption></figcaption></figure>

{% hint style="info" %}
A manual **Verify scope** runs on the contract's scan definition, not on the scope's own scan definition. Its results go to the checks of the scope as usual.
{% endhint %}

To run a scope from the Soda CLI, filter on `scope`:

```bash
# Run the checks of the scope finance
soda contract verify --dataset snowflake/analytics/public/orders --publish --use-runner --soda-cloud sc_config.yml -cf scope=finance

# Run only the checks without a scope
soda contract verify --dataset snowflake/analytics/public/orders --publish --use-runner --soda-cloud sc_config.yml -cf scope=base
```

> Learn more in [Verify a contract](/soda-documentation/data-testing/cloud-managed-data-contracts/verify-a-contract.md) and [Contract Language reference](/soda-documentation/reference/contract-language-reference.md#scoped-check-execution).

To run a scope through the [REST API](broken://pages/qOmWPvixdxBUojZ2sOEb), call `POST /api/v1/contracts/{contractId}/verify` with `checkFilters`. The only field is `scope`. Use `base` for the checks without a scope, and set `negate: true` to run every scope except the ones listed:

```json
{
  "checkFilters": [
    { "field": "scope", "values": ["finance", "base"] }
  ]
}
```

This example verifies the checks of the `finance` scope and the checks without a scope.

A scope with a schedule can also be started with the submit scan API, using the name of its scan definition, such as `scope.finance:snowflake/analytics/public/orders`.

{% hint style="warning" %}
Running scopes on a Soda Runner requires `Soda Runner 2.0.18` or later (equivalent to `Soda Agent 1.4.32`). An older runner ignores the scope selection and verifies the whole contract, on a manual **Verify scope** as well as on the scheduled runs of the contract and of each scope.
{% endhint %}

## Notifications

### Route notifications by scope

To send the results of a scope to its owners, add a **Contract scope** condition to a notification rule:

{% stepper %}
{% step %}
Create or edit a notification rule and set **Applies to** to **Specific Checks**. See [Notifications](/soda-documentation/manage-issues/notifications.md).
{% endstep %}

{% step %}
Under **Alert Conditions**, select the attribute **Contract scope**, choose the condition **equals** or **in**, and enter one or more scope keys as the value. Enter `base` to match the checks without a scope.

<figure><img src="https://1123167021-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FA2PmHkO5cBgeRPdiPPOG%2Fuploads%2Fgit-blob-fe2a4df08aa82b1e261fde6d6606d6172f20356c%2Fcontract-scopes-notification-rule.png?alt=media" alt="Add Notification Rule dialog applying to Specific Checks with the condition Contract scope equals finance" width="563"><figcaption></figcaption></figure>
{% endstep %}
{% endstepper %}

Keep in mind:

* The value is the scope **key**, not its name. The comparison is exact and case-sensitive, and scope keys are lowercase: `Finance` does not match the scope `finance`.
* A scope key matches that scope **in every contract** of your organization. To target the scope of a single dataset, add a dataset condition to the rule.
* The condition is available on check rules only, and matches contract checks only. Data Standard checks and metric monitors never match it.
* In the [REST API](broken://pages/qOmWPvixdxBUojZ2sOEb), the condition is the `contractScopeKey` field. A rule created through the API with this condition inside a `not`, or inside an `or` with more than one condition, cannot be saved from the rule form in Soda Cloud. Edit such a rule through the API.

### Scope label in notifications

Contract notifications in Slack, Microsoft Teams and e-mail list the checks of the verification, one line per check. When a check belongs to a scope, its line ends with a scope label: the text `· scope` followed by the scope key. For example, a failed check in the scope `finance` ends with `· scope finance`.

In every channel, the scope key in the label is a link. It opens the dataset's **Checks** tab with that scope's pill selected, so you see only the checks of that scope. The check name keeps its usual link to the check.

The lines of checks without a scope and of Data Standard checks have no scope label. Notifications don't group checks by scope: the lines stay in their usual order.

## Limitations

* Scopes can't have an owner yet. To record who owns a scope, use a check attribute in the scope's `check_attributes`.
* The **Scope** filter and the pills are available on the dataset's Checks tab only, not on the organization-wide Checks page or on dashboards.
* The [REST API](broken://pages/qOmWPvixdxBUojZ2sOEb) does not return the scopes of a contract, and cannot filter checks or results by scope. You can only select which scopes to verify.
* Data Standards do not support scopes.
* Scopes cannot be ordered or chained: the checks of one scope can't wait for the result of another scope.

<br>

***

{% hint style="info" %}
You are **not logged in to Soda** and are viewing the default public documentation. Learn more about [Licensing & documentation access](/soda-documentation/reference/documentation-access-and-licensing.md).
{% endhint %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.soda.io/soda-documentation/data-testing/cloud-managed-data-contracts/contract-scopes.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
