Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 156 additions & 0 deletions CLAUDE.md

Large diffs are not rendered by default.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion docs/data-quality-checks/overview-of-a-check.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Checks Overview
# :material-book-alphabet:{ .middle style="color: var(--q-brick)" } Checks Overview

Checks in Qualytics are rules applied to data that ensure quality by validating accuracy, consistency, and integrity. Each check includes a data quality rule, along with filters, tags, tolerances, and notifications, allowing efficient management of data across tables and fields.

Expand Down
158 changes: 158 additions & 0 deletions docs/data-quality-checks/ownership/api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
# :material-api:{ .middle style="color: var(--q-brick)" } Quality Check Ownership API

This page documents how to read and change the **Owner** of a quality check via the REST API. Ownership is exposed on the standard Quality Checks endpoints (there is no dedicated ownership route) through a single `owner_id` field on create/update payloads and query parameters on the list endpoints (`owner_id` on `GET /quality-checks`, `owner=true` on `GET /quality-checks/listing`). All endpoints share the base URL of your deployment (for example, `https://your-instance.qualytics.io/api`) and require a Bearer token.

!!! tip
For complete API documentation, including request and response schemas, visit the [API docs](https://demo.qualytics.io/api/docs){:target="_blank"}.

!!! note "Permissions"
All endpoints require the **Member** role. Reads need the **Viewer** team permission on the check's datastore; writes (create, update, bulk update) need the **Author** team permission. The user targeted by `owner_id` must independently have at least the **Drafter** team permission on the same datastore.

## The `owner_id` field

Every quality check carries a single owner. The API exposes it as `owner_id` on writes and as a nested `owner` object on reads.

| Field | Type | Description |
| :--- | :--- | :--- |
| `owner_id` | `int` | The user ID of the check owner. On `POST`, defaults to the caller (or to the `Qualytics` user on [AI Managed (inferred) checks](deep-dive/how-it-works.md#defaults-at-creation-time){:target="_blank"}). On `PUT`/`PATCH`, omit (or send `null`) to leave the owner unchanged. The target user must have at least the **Drafter** team permission on the check's datastore. |

??? example "Example `owner` field on a read response (abbreviated)"

```json
{
"id": 101,
"rule_type": "unique",
"inferred": false,
"owner": {
"id": 42,
"name": "Alice Lee",
"email": "alice@example.com"
}
}
```

## Create

### Create a check with an explicit owner

**Endpoint:** `POST /api/quality-checks`

**Permission:** Member role + **Author** team permission on the container's datastore. The target `owner_id` must have **Drafter** on the same datastore.

Include `owner_id` in the create payload to set an explicit owner. Omit the field to let the platform default to the caller.

??? example "Example request"

```bash
curl -X POST "https://your-instance.qualytics.io/api/quality-checks" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"description": "Ensure C_NAME is unique in the CUSTOMER table",
"rule": "unique",
"fields": ["C_NAME"],
"container_id": 145,
"owner_id": 42
}'
```

## Update

### Transfer ownership on a single check

**Endpoint:** `PUT /api/quality-checks/{id}`

**Permission:** Member role + **Author** team permission on the check's datastore. The target `owner_id` must have **Drafter** on the same datastore.

Send `owner_id` set to the new owner's user ID. The previous owner is recorded in the check's history.

The `PUT` endpoint requires `description`; include the check's current description (or a new one if you also want to update it) along with `owner_id`.

??? example "Example request"

```bash
curl -X PUT "https://your-instance.qualytics.io/api/quality-checks/101" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"description": "Ensure C_NAME is unique in the CUSTOMER table",
"owner_id": 42
}'
```

!!! warning "AI Managed conversion still applies"
For AI Managed (inferred) checks owned by Qualytics, editing any *assertion property* (`properties`, `coverage`, `filter`, or `fields`) in the same payload also transfers ownership to the editor and flips `inferred` from `true` to `false`. Sending `owner_id` without modifying any assertion property transfers ownership and keeps `inferred` as `true`.

### Transfer ownership across many checks

**Endpoint:** `PATCH /api/quality-checks`

**Permission:** Member role + **Author** team permission on the datastore of every check in the request. The target `owner_id` must have **Drafter** on each of those datastores.

Send a list of `{ id, owner_id }` entries; the same `owner_id` can be reused across entries to apply one owner to many checks.

!!! note "Bulk is all-or-nothing"
If any entry references a check the caller cannot update (404 not found, missing **Author** on the datastore, or the target `owner_id` lacks **Drafter**), the entire request is rejected and no checks are updated. Retry with the offending entry removed.

??? example "Apply the same owner to many checks"

```bash
curl -X PATCH "https://your-instance.qualytics.io/api/quality-checks" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '[
{ "id": 101, "owner_id": 42 },
{ "id": 102, "owner_id": 42 },
{ "id": 103, "owner_id": 42 }
]'
```

## Filter

### Filter checks by a specific owner

**Endpoint:** `GET /api/quality-checks`

**Permission:** Member role. Results are scoped to datastores the caller can see.

Use the `owner_id` query parameter to narrow the list to checks owned by a specific user. Combine with the other list filters (status, tags, rule type, and so on) to scope further.

??? example "List checks owned by user 42"

```bash
curl -X GET "https://your-instance.qualytics.io/api/quality-checks?owner_id=42" \
-H "Authorization: Bearer YOUR_TOKEN"
```

### List checks owned by the current user

**Endpoint:** `GET /api/quality-checks/listing`

**Permission:** Member role.

The aggregated listing endpoint accepts `owner=true`, which scopes the result to checks owned by the user issuing the request. This is the same filter the UI uses for the **Owned** subtab.

??? example "Equivalent of the Owned subtab"

```bash
curl -X GET "https://your-instance.qualytics.io/api/quality-checks/listing?owner=true&count_perspective=checks" \
-H "Authorization: Bearer YOUR_TOKEN"
```

## Error responses

| Status | Description |
| :--- | :--- |
| `400 Bad Request` | A business-logic conflict prevents the operation (for example, attempting to change properties while the check status is invalid or discarded). |
| `401 Unauthorized` | Missing or invalid API token. |
| `403 Forbidden` | The caller lacks **Author** on the check's datastore, or the target `owner_id` lacks **Drafter** on the same datastore. |
| `404 Not Found` | The check ID or `owner_id` user does not exist. |
| `422 Unprocessable Entity` | The payload fails schema validation (for example, a required field such as `description` is missing on `PUT`, `owner_id` is not an integer, or `properties` contains an unrecognized key for the rule type). |

## Related

- [Introduction](deep-dive/introduction.md){:target="_blank"}: overview of ownership and the three transfer modes at a glance.
- [How It Works](deep-dive/how-it-works.md){:target="_blank"}: full mechanics: defaults, the three transfer modes in detail, permissions, notifications, history, and filtering.
- [Change Check Owner](how-tos/change-owner.md){:target="_blank"}: UI walkthrough that mirrors `PUT /api/quality-checks/{id}`.
- [Bulk Change Check Owner](how-tos/bulk-change-owner.md){:target="_blank"}: UI walkthrough that mirrors `PATCH /api/quality-checks`.
- [FAQ](faq.md){:target="_blank"}: short answers to the most common ownership questions.
66 changes: 66 additions & 0 deletions docs/data-quality-checks/ownership/deep-dive/examples.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# :material-text-box-edit-outline:{ .middle style="color: var(--q-brick)" } Quality Check Ownership Examples

Real-world scenarios that show how check ownership is typically used in production. Each example combines the same building blocks: defaults at creation, transfer modes, filtering, and notifications.

## Onboarding a new data engineer

**Context.** A new data engineer joins the team and is added to the **Customer Data** datastore with the **Drafter** team permission. They will gradually take over a set of AI Managed checks that have been running since the datastore was profiled, all still owned by the `Qualytics` user.

**What happens.**

1. The engineer opens the datastore's **Quality Checks** tab and applies the **Owner** filter, picking `Qualytics` from the top of the dropdown. The list narrows to every AI Managed check that no one has taken over.
2. For each check they review, they open the **Edit Check** form and change the **Owner** to themselves. The inline warning **"Once transferred, ownership cannot be returned to Qualytics"** appears in the dropdown.
3. After saving, the **Owner** filter is updated to their own user (via the **Owned** subtab) so they can keep a personal queue of the checks they are now responsible for.

**Why it works.** AI Managed checks default to `Qualytics`, so they are easy to find by filter. The explicit transfer requires only the **Drafter** permission on the datastore for the new owner, which the engineer already has, and the **Author** permission for the user making the change.

## Reassigning a teammate's checks

**Context.** An engineer is rotating off the team. Their Authored checks on the **Orders** datastore need to move to a new owner before they lose access.

**What happens.**

1. The team lead opens **Explore > Checks**, applies the **Owner** filter, and picks the leaving engineer's name. The list narrows to every check they currently own.
2. The team lead selects all the checks via the row checkboxes. The selection toolbar shows the total count.
3. They open the **bulk menu :material-dots-vertical:{ style="color: var(--md-default-fg-color)" }** in the toolbar and click **Edit :material-square-edit-outline:{ style="color: var(--md-default-fg-color)" }**. The **Bulk Edit Checks** modal opens; they toggle on the **Owner** row and pick the new owner from the dropdown.
4. After **Save**, every check is updated to the new owner. Each check's **History** records the bulk change with the lead's name as the user who made it.
5. The new owner sees the checks under their **Owned** subtab on the next page load.

**Why it works.** The **Owner** filter scopes the list to a single user before bulk selection, eliminating the risk of accidentally reassigning unrelated checks. The bulk endpoint replaces (not merges) the owner, which is the intended behavior in a handoff.

## Coordinating edits with ownership notifications

**Context.** Two engineers maintain a shared set of checks on the **Inventory** datastore. They want to know when the other person edits one of their checks so they can keep their downstream dashboards in sync.

**What happens.**

1. Each check is owned by one of the two engineers; ownership was assigned explicitly when the checks were authored.
2. When engineer A edits the **coverage** of a check owned by engineer B, the platform creates an **Ownership** in-app notification for engineer B: *"Engineer A updated a check you own"* (`#482` • Volumetrics).
3. Engineer B opens the notification, sees the change in the check's **History**, and updates their dashboard query the same day.

**Why it works.** The notification fires whenever the updater is not the owner, so every cross-edit produces a signal. Self-edits stay silent, which keeps the notification feed focused on edits the owner did not make.

## Auditing AI Managed checks before a compliance review

**Context.** Ahead of a quarterly compliance review, the data platform team needs a clean inventory of which checks are still managed by `Qualytics` (and therefore subject to Profile refreshes) versus those that have been explicitly taken over.

**What happens.**

1. A team admin opens **Explore > Checks** and sets the **Owner** filter to `Qualytics`. The list shows every check still managed by `Qualytics` across all datastores they have access to.
2. They export the list (or call `GET /api/quality-checks?owner_id=<qualytics_user_id>`) and share it with the compliance reviewer.
3. The reviewer flags the subset that should be locked from Profile refreshes. For each flagged check, the admin opens the **Edit Check** form, transfers ownership to a named user, and saves. If any assertion property is also edited during the same save, the check converts to Authored; ownership transfer alone (no assertion changes) keeps `inferred: true` while attributing responsibility to a human owner.

**Why it works.** The `Qualytics` user is treated like any other owner in the filter dropdown, so the audit becomes a single filter operation. The transfer-only path (without editing assertion properties) is the Explicit transfer flow documented in [How It Works · Explicit transfer](how-it-works.md#explicit-transfer-edit-form-or-bulk-update){:target="_blank"} and the [API · `owner_id` field notes](../api.md#the-owner_id-field){:target="_blank"}.

## Draft promotion with automatic owner reassignment

**Context.** A Drafter-level reviewer creates a quality check as a Draft for review by a senior engineer. The senior engineer holds the **Author** team permission; the Drafter does not.

**What happens.**

1. The Drafter authors the check and saves it as **Draft**. They are recorded as the owner because they created it.
2. The senior engineer opens the check, validates it, and flips the **Status** from **Draft** to **Active**.
3. Because the Drafter cannot manage an Active check, the **Drafter activation transfer** fires automatically: the owner is reassigned from the Drafter to the senior engineer at the same save.
4. The check's **History** records both events on the same timeline entry: the status change *and* the ownership reassignment.

**Why it works.** Drafter activation transfer prevents an Active check from being stuck with an owner who cannot edit it. The pattern keeps the audit trail accurate: the senior engineer ends up as the responsible owner the moment the check goes live.
Loading
Loading