# Tutorial — your first suite

A hands-on walkthrough: from an empty workspace to a check that runs and alerts, in a few
minutes. It assumes DataQ is already running and you can sign in (if you're standing up the
app itself, see [Getting started](install.md) and [Deployment](../operate/deployment.md) first).

By the end you'll have connected a datasource, authored a suite with a freshness + a
value check, run it, read the result, and wired an alert.

## 0. Sign in

Open the app URL and sign in — the local/eval stack defaults to an **email one-time code**
(check the bundled Mailpit inbox at `http://localhost:8025`); a production deployment
typically uses your **identity provider (SSO)**. See
[Getting started](install.md#choosing-an-auth-mode) for the three sign-in modes. You land on
the **Dashboard** — empty for now, with a **Get started** panel listing the four steps this
tutorial walks through (connect a data source, create a suite, add a check, run it). The
panel offers the next step you are allowed to take, is the same for everyone in the
workspace, and goes away once all four are done. Once suites have run the dashboard looks
like this:

![The monitoring dashboard: asset health, integrity score, pass rate and per-suite performance](../assets/screenshots/dashboard.png){ .screenshot }

## 1. Connect a datasource

> **This step needs the Admin role.** Connections are shared infrastructure holding
> credentials, so only workspace **Admins** can create, edit, delete, or re-credential one
> (ADR [0033](../adr/0033-workspace-roles-rbac.md)). If you're a Member or Viewer, ask an Admin
> to set the connection up — then pick this tutorial back up at
> [step 2](#2-create-a-suite-and-point-it-at-a-target).

1. Go to **Connections → Add connection**.

    <video class="clip" autoplay loop muted playsinline poster="../../assets/videos/add-connection.jpg">
      <source src="../../assets/videos/add-connection.mp4" type="video/mp4">
    </video>

    *Adding a Snowflake connection, start to finish (12 s)*

2. Pick your datasource type (Snowflake, Unity Catalog, ADLS Gen2, S3, or Apache
   Iceberg). The form is spec-driven — it asks only for what that type needs.

    ![Choosing a datasource type: warehouses, lakehouses, cloud storage, and the optional orchestration providers](../assets/screenshots/connection-type-picker.png){ .screenshot }

    ![The Snowflake connection form asks only for what Snowflake needs](../assets/screenshots/connection-form-snowflake.png){ .screenshot }

3. Fill it in with a **read-only** credential (for Snowflake, key-pair auth is recommended),
   then click **Test**. A green result means DataQ can reach it.

*Tip:* name it for its environment — e.g. `snowflake-prod` — so a suite's env is unambiguous.

*Later:* moving an existing connection to a different host — a new Snowflake `account`, a new
`endpoint_url` — requires re-entering the credential in the same save. The form asks for it as
soon as you change one of those fields; a stored credential is never sent to a destination you
changed without it.

## 2. Create a suite and point it at a target

1. Go to **Suites → New suite**, give it a name (e.g. `orders — snowflake prod`), and pick
   the connection you just made.

2. Set the **run target**: a table (SQL datasources) or a file / batch pattern (flat files).

3. Save. The suite is empty — let's add checks. A finished suite looks like this:

    ![A suite: its connection and target, the checks with their dimension tags, and the pipeline triggers panel](../assets/screenshots/suite-detail.png){ .screenshot }

## 3. Add your first checks

Start with the checks that catch the incidents that actually page people — *"did the load
run?"* and *"did it land whole?"* — before any value-level rule.

1. **A freshness monitor.** Add a check → **Freshness**, on the table's load/updated
   timestamp column. Set a **fail** threshold (e.g. *fail if > 26 hours old* for a daily
   load). Freshness/volume monitors require a fail or critical threshold.

    ![Add check: pick a kind first — column values, freshness, volume, schema drift, anomaly, comparison or custom SQL](../assets/screenshots/check-editor-picker.png){ .screenshot }

    ![A freshness monitor: timestamp column plus warn / fail / critical age thresholds](../assets/screenshots/check-editor-freshness.png){ .screenshot }

2. **A value check.** Add a check → a GX expectation like
   `expect_column_values_to_not_be_null` on a key column (e.g. `order_id`).

    <video class="clip" autoplay loop muted playsinline poster="../../assets/videos/author-check.jpg">
      <source src="../../assets/videos/author-check.mp4" type="video/mp4">
    </video>

    *Authoring a value check and reaching the dry-run preview (10 s)*

3. Before saving the value check, click **Dry-run** to preview it against live data — you'll
   see the observed unexpected-% so you can set a **warn** threshold just above today's
   baseline (so it's green now, loud only when reality changes).

    ![A value check: the expectation, its column, severity thresholds and the dry-run preview button](../assets/screenshots/check-editor-expectation.png){ .screenshot }

*Tip:* use the **column profiler** on a column to see nulls / distinct / min-max / top values
while you decide what to check.

## 4. Run it

Open the suite's **Run** panel and click **Run now**. Watch the **live per-check progress** —
each check resolves to pass / warn / fail / critical (or the operational `skip` / `error`).
You can **cancel** a run mid-flight.

## 5. Read the result

Go to **Results** and open the run:

<video class="clip" autoplay loop muted playsinline poster="../../assets/videos/read-results.jpg">
  <source src="../../assets/videos/read-results.mp4" type="video/mp4">
</video>

*Opening a run and expanding a failing check (6 s)*

- Per-check outcomes with **observed vs expected** values.
- For a failing check, expand it to see the **failing-row sample** — **redacted**
  column-aware (PII masked; the counts and shape kept).
- The **Dashboard** now shows a health score, pass rate, and the start of a trend.

If a run *failed to execute* (bad credential, unreachable store), it shows a plain-language
**failure reason**, not just "failed".

![The Results page: every run with its outcome, trigger and duration](../assets/screenshots/results-list.png){ .screenshot }

![A run: per-check status, the measured metric and the observed value; a critical check has opened an incident](../assets/screenshots/run-detail.png){ .screenshot }

## 6. Get alerted

The short version is below; the full walk-through with screenshots is
[Your first alert](first-alert.md).

1. On the suite, open **Notifications** and add a channel — Teams, Slack, or email — and pick
   a threshold (the default **warn-and-worse** is a good start).

2. Now a breaching run notifies you, with a deep link back to the run and the
   expected-vs-observed context. **Dedup** means you hear about a breakage once (and again if
   it escalates), not on every run.

## 7. Automate it

The short version is below; the full walk-through is [Run it automatically](connect-pipeline.md).

- **If an orchestrator loads this table** (ADF / Airflow / dbt): open the suite's **Triggers**
  and bind it to the pipeline — the suite runs right after the pipeline succeeds, and results
  correlate to the pipeline run. See [Orchestration](../guides/orchestration.md).
- **Otherwise:** open **Schedules** and set a cron cadence matched to when the data arrives.
  See [Scheduling](../guides/scheduling.md).

## Where to next

- [Recommended usage](../guides/recommended-usage.md) — activate the rest of the features the right way.
- [Best practices](../guides/best-practices.md) — the ongoing "signal, not noise" discipline.
- [AI assistants (MCP setup)](../guides/mcp-setup.md) — drive all of this from Claude / Copilot / Cursor.
