# Audit and clean up draft checks

Source: https://userguide.qualytics.io/cli/examples/audit-draft-checks/

## Goal

Review the queue of `Draft` checks (typically AI Managed checks awaiting review), promote the ones you want to keep, and archive the rest. This is a recurring governance task whenever you run profiling with `--infer-as-draft` or whenever a team prefers a review gate before checks go live.

## Permissions

| Step | Endpoint | Role | Team permission |
| --- | --- | --- | --- |
| List checks (filter by status) | `GET /api/quality-checks` | `Member` | `Reporter` |
| Get a single check | `GET /api/quality-checks/{id}` | `Member` | `Reporter` |
| Activate (promote to Active) | `PUT /api/quality-checks/{id}` (status=Active) | `Member` | `Author` |
| Archive (soft delete) | `DELETE /api/quality-checks/{id}?archive=true` | `Member` | `Author` |
| Bulk archive | `DELETE /api/quality-checks?ids=...` | `Member` | `Author` |

## Prerequisites

- The CLI is installed and authenticated.
- At least one datastore has draft checks (run `qualytics operations profile --datastore-id N --infer-as-draft` if you need test data).

## CLI workflow

```mermaid
graph LR
    L[list --status Draft] --> R[Review each]
    R -->|Keep| A[activate --ids]
    R -->|Drop| D[delete --ids --archive]
    A --> Active[Now running in scans]
    D --> Archived[Archived; reversible]
```

### 1. List draft checks

```bash
qualytics checks list --datastore-id 42 --status Draft
```

Or as JSON for scripting:

```bash
qualytics checks list --datastore-id 42 --status Draft --format json
```

### 2. Inspect specific candidates

```bash
qualytics checks get --id 501
qualytics checks get --id 501 --format json | jq '.description, .properties'
```

### 3. Activate the keepers

```bash
qualytics checks activate --ids "501,502,503"
```

### 4. Archive the rest

```bash
qualytics checks delete --ids "510,511,512" --archive
```

`--archive` is the default; checks go to `Archived` status and stop running. They can be brought back with `activate`.

## Behind the scenes

| CLI step | Method | Path |
| --- | --- | --- |
| `checks list --status Draft` | GET | `/api/quality-checks?datastore_id={id}&status=Draft` |
| `checks get --id` | GET | `/api/quality-checks/{check_id}` |
| `checks activate --ids` | PUT | `/api/quality-checks/{check_id}` (per id; sets `status: Active`) |
| `checks delete --ids --archive` | DELETE | `/api/quality-checks?ids=...` (with archive=true) |
| `checks delete --ids --no-archive` | DELETE | `/api/quality-checks?ids=...` (hard delete, irreversible) |

## Python equivalent

```python
import os
import httpx

BASE_URL = os.environ["QUALYTICS_URL"].rstrip("/")
TOKEN    = os.environ["QUALYTICS_TOKEN"]
HEADERS  = {"Authorization": f"Bearer {TOKEN}"}
DATASTORE_ID = 42

with httpx.Client(headers=HEADERS, timeout=30.0) as client:
    drafts = client.get(
        f"{BASE_URL}/api/quality-checks",
        params={"datastore_id": DATASTORE_ID, "status": "Draft"},
    ).json()

    keep_ids   = []
    discard_ids = []
    for c in drafts:
        # In practice you'd present these to a reviewer or run heuristics
        if c["rule_type"] in ("isNotNull", "isUnique"):
            keep_ids.append(c["id"])
        else:
            discard_ids.append(c["id"])

    for cid in keep_ids:
        client.put(
            f"{BASE_URL}/api/quality-checks/{cid}",
            json={"status": "Active"},
        ).raise_for_status()

    if discard_ids:
        client.request(
            "DELETE",
            f"{BASE_URL}/api/quality-checks",
            params={"ids": ",".join(str(i) for i in discard_ids), "archive": "true"},
        ).raise_for_status()

    print(f"activated {len(keep_ids)}, archived {len(discard_ids)}")
```

## Variations and advanced usage

### Audit by container or rule type

```bash
qualytics checks list --datastore-id 42 --status Draft --containers "10,20"
qualytics checks list --datastore-id 42 --status Draft --tags "ai-managed"
```

The CLI doesn't have a direct `--rule-type` filter on list; for that, use `--format json | jq 'select(.rule_type=="X")'`.

### Bulk activate everything that meets a criterion

```bash
ALL_DRAFT_IDS=$(qualytics checks list --datastore-id 42 --status Draft --format json \
                | jq -r '.[] | select(.rule_type=="isNotNull") | .id' \
                | paste -sd, -)

qualytics checks activate --ids "$ALL_DRAFT_IDS"
```

### Schedule a weekly review

A simple wrapper script that emails or Slacks the draft list every Monday:

```bash
qualytics checks list --datastore-id 42 --status Draft --format json \
  | jq -r '.[] | "[\(.id)] \(.rule_type) on \(.container.name): \(.description // "(no description)")"' \
  | mail -s "Qualytics drafts to review" data-quality@example.com
```

## Troubleshooting

| Symptom | Likely cause | Fix |
| --- | --- | --- |
| List is empty even though profile created drafts | Wrong datastore ID, or AI Effort was set to `off` | `qualytics operations list --datastore-id 42 --type profile` to confirm the last run produced drafts. |
| `activate` fails with 403 | Team permission below `Author` | Ask a team admin to grant `Author`. |
| `delete --no-archive` removed a check you wanted | Hard delete is permanent | Recreate from a previous export, or rebuild from web app history if available. |
| Some `activate` calls succeed, others 422 | Underlying check has stale field references (table changed) | Re-sync the datastore; the affected checks may need to be deleted and regenerated by the next Profile run. |

## Related

- [Quality Checks command reference](https://userguide.qualytics.io/cli/quality-checks/)
- [AI Managed Checks](https://userguide.qualytics.io/data-quality-checks/checks/deep-dive/ai-managed-checks/): the conceptual model behind the checks you're reviewing.
- [Bulk-create quality checks](https://userguide.qualytics.io/cli/examples/bulk-create-quality-checks/): create checks straight to `Active` and skip the draft step entirely.
- [Daily sync, profile, and scan](https://userguide.qualytics.io/cli/examples/daily-pipeline/): use `profile --infer-as-draft` to feed the review queue.

*Last updated: November 10, 2026.*
