> ## Documentation Index
> Fetch the complete documentation index at: https://docs.duvo.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Cases and Queues

> Inspect Case history, bulk-reprocess Cases on an Agent, and manage labels with the Duvo CLI.

A Case is a single piece of work delivered into a Queue for an Agent to pick up. The CLI lets you inspect Case history, re-process Cases in bulk, manage labels, and view the Agents bound to a Queue.

For an overview of the Queue system, see [Queue](/user-guide/assignment-features/case-queue).

## Cases (`duvo cases`)

### Inspect a Case's history

```bash theme={"dark"}
duvo cases runs <case-id>                                    # list all Runs that have worked on a Case
```

### Create Cases

Create one Case with `--title`, or many at once with `--from-file` (a JSON file holding a single Case object or an array of up to 100). Pass `-` to `--from-file` to read the array from stdin.

```bash theme={"dark"}
duvo cases create --queue <queue-id> --title "Urgent refund" --data "Customer wants a full refund."
duvo cases create --queue <queue-id> --from-file cases.json  # a JSON array of Case objects
```

On a Queue with a Case schema, send a structured payload instead of free-text `data`. Use `--json-data` for one Case, or a `json_data` object per Case in `--from-file`. The payload is validated against the Queue's schema, and `data` and `json_data` are mutually exclusive — a request is either all free-text or all structured.

```bash theme={"dark"}
duvo cases create --queue <queue-id> --title "Invoice INV-100" \
  --json-data '{"invoice_id":"INV-100","amount":1250}'
duvo cases create --queue <queue-id> --from-file typed-cases.json  # each Case carries "json_data"
```

<Note>
  A schema-guarded Queue accepts only structured Cases: it needs a Case schema, and you send `json_data` validated against it — free-text `data` is rejected. A Queue without the guard accepts free-text `data` (and also `json_data` when it has a schema). Either way, you no longer need to turn the schema guard off to add Cases by hand.
</Note>

### Edit a Case

Update a Case's title and/or its free-form data. Provide at least one field. Titles can be up to 500 characters.

```bash theme={"dark"}
duvo cases update <case-id> --title "Refund request #1234"    # rename a Case
duvo cases update <case-id> --data "Customer wants a full refund."  # replace the Case data
duvo cases update <case-id> --title "New title" --data "New data"   # both at once
duvo cases set-fields <case-id> --set '$.invoice.amount=1234.5' --set '$.invoice.currency="EUR"'  # set typed json_data fields in one atomic write
duvo cases set-fields <case-id> --from-file fields.json             # a JSON array of {"path":"$.…","value":…}
duvo cases set-fields <case-id> --set '$.lines[2].amount=20' --expect '$.lines[2].id="L-3"'  # change one array item, only if index 2 is still L-3
```

<Note>
  Editing a Case works whether it is pending, in progress, or already settled. Status, priority, and labels have their own commands.
</Note>

### Bulk-reprocess Cases

Re-process 1–100 Cases on a specific Agent in one call.

<Warning>
  Any active Runs for these Cases are interrupted.
</Warning>

<CodeGroup>
  ```bash With confirmation theme={"dark"}
  duvo cases bulk-reprocess --queue <queue-id> \
    --agent <agent-id> \
    --ids <case-id-1>,<case-id-2>,<case-id-3>
  ```

  ```bash Skip confirmation theme={"dark"}
  duvo cases bulk-reprocess --queue <queue-id> \
    --agent <agent-id> \
    --ids <case-id-1>,<case-id-2> --yes                         # skip the confirmation prompt
  ```
</CodeGroup>

### Set Case priority

Raise or clear the priority of 1–100 Cases so higher-priority work is picked up first among eligible pending Cases. Priority levels are `none` (the default), `medium`, and `high`. Due postponed Cases are still picked up before priority ordering applies.

```bash theme={"dark"}
duvo cases bulk-update-priority --queue <queue-id> \
  --ids <case-id-1>,<case-id-2> --priority high              # raise priority
duvo cases bulk-update-priority --queue <queue-id> \
  --ids <case-id-1> --priority none --yes                    # clear priority, skip confirmation
```

You can also set a priority when creating a Case, and filter the list by priority:

```bash theme={"dark"}
duvo cases create --queue <queue-id> --title "Urgent refund" --priority high
duvo cases list --queue <queue-id> --priority high,medium     # comma-separated levels
```

### Filter by status

`--status` takes display-status buckets — the same groupings the Queue view
offers — rather than the raw per-Case status:

```bash theme={"dark"}
duvo cases list --queue <queue-id> --status pending
duvo cases list --queue <queue-id> --status needs_review,canceled   # comma-separated buckets
```

| Bucket | Display statuses it matches | Raw `status` behind them |
| - | - | - |
| `all` | every Case | any |
| `pending` | `pending` | `pending` |
| `processing` | `in_progress`, `evaluating` | `pending` (claimed by a Run), `completed` (evaluation running) |
| `needs_input` | `needs_input` | `pending` |
| `postponed` | `postponed` | `pending` |
| `needs_review` | `issues`, `failed` | `completed`, `failed` |
| `resolved` | `success`, `completed` | `completed` |
| `canceled` | `canceled` | `failed` |

<Note>
  Each Case in `--json` output carries two fields. `status` is the raw
  settlement column (`pending`, `completed`, `failed`). `display_status` is the
  ten-state value the table's STATUS column shows: a `pending` Case displays as
  `pending`, `in_progress`, `needs_input`, or `postponed` depending on its live
  state; a settled Case displays its stored outcome, or `evaluating` while the
  evaluation still runs. `--status` filters on `display_status`, so a bucket count
  does not equal a raw-`status` count. The CLI rejects the aliases `claimed`,
  `completed`, and `failed`; use `processing`, `resolved`, and `needs_review`
  instead.
</Note>

### Filter by issue severity

When a Case evaluation flags failing rubrics, the Case records the highest
severity it hit. Filter on it to triage the worst results first:

```bash theme={"dark"}
duvo cases list --queue <queue-id> --issue-severity critical           # only critical issues
duvo cases list --queue <queue-id> --issue-severity critical,medium    # comma-separated levels
```

Severity narrows *within* evaluated Cases that found issues, so it composes with
a status filter but returns nothing alongside a status that excludes them:

```bash theme={"dark"}
duvo cases list --queue <queue-id> --status needs_review --issue-severity critical
```

<Note>
  Only `critical` and `medium` are selectable. A Case whose failing rubrics are all
  low severity is recorded as a success — low findings still appear in the Case's
  rubric breakdown, but they don't hold the Case back, so no Case carries a `low`
  severity to filter on.
</Note>

### Filter by your own pending approvals

When a Case is waiting on approvers, `--awaiting-my-approval` narrows the list to
the Cases holding a decision that is yours to make:

```bash theme={"dark"}
duvo cases list --queue <queue-id> --awaiting-my-approval
```

"Mine" is resolved from the credential you are calling with, never from a name
you pass — an API key filters as the user who owns it. A credential with no user
behind it is rejected rather than returning an empty list.

It narrows within Cases that are blocked on a human, so it composes with
`--status needs_input` and returns nothing alongside a settled status:

```bash theme={"dark"}
duvo cases list --queue <queue-id> --status needs_input --awaiting-my-approval
```

<Note>
  Only Cases in Queues that use approvals can match, so this returns nothing for a
  team that does not have Case approvals in use.
</Note>

Use creation-time bounds to inspect Cases added during a fixed window:

```bash theme={"dark"}
duvo cases list --queue <queue-id> \
  --created-at-from 2026-08-01T00:00:00Z \
  --created-at-to 2026-08-02T00:00:00Z
```

Use update-time bounds to inspect Cases changed during a fixed window:

```bash theme={"dark"}
duvo cases list --queue <queue-id> \
  --updated-at-from 2026-08-01T00:00:00Z \
  --updated-at-to 2026-08-02T00:00:00Z
```

For both pairs, the lower bound is inclusive and the upper bound is exclusive. Adjacent windows do not count the same Case twice.

Add `--count-only` when you need the matching total without Case rows. All Case filters still apply.

```bash theme={"dark"}
duvo cases list --queue <queue-id> --status pending --count-only
# Total Cases: 42
```

<Note>
  Setting priority never interrupts a Run or changes a Case's status — it only affects the order pending Cases are picked up in.
</Note>

### Search Cases with label and structured filters

`duvo cases list` uses a query-string endpoint, so it can't express label
filters. `duvo cases search` calls the JSON-body
`POST /queues/{queue_id}/cases/search` endpoint and carries the same status,
issue-severity, priority, date-range, sort, `--field`, `--count-only`,
`--limit`, `--offset`, and `--json` filters as `list`, plus one that `list`
can't send:

* `--label` narrows to Cases carrying specific labels. Accepts `key=value` for
  keyed labels or a bare `value` for tag-style labels, matching
  `duvo cases labels assign`/`unlink`. Repeat the flag to combine labels:
  repeated values for the same key are ORed, while different keys are ANDed.

`--field` works the same way in both commands: it filters on a field of a typed
Queue's `json_data`, as `<path>:<operator>[:<value>]` (for example
`$.invoice.amount:gte:5000`). Supported operators are `equals`, `notEquals`,
`in`, `notIn`, `gt`, `gte`, `lt`, `lte`, `contains`, `isSet`, and `isNotSet`.
Repeatable, and all entries must match.

```bash theme={"dark"}
duvo cases search --queue <queue-id> --label "priority=urgent"        # single keyed label
duvo cases search --queue <queue-id> \
  --label "priority=urgent" --label "priority=high" \
  --label "region=eu"                                                 # (urgent OR high) AND region=eu
duvo cases search --queue <queue-id> \
  --field '$.invoice.amount:gte:5000' \
  --field '$.supplier:equals:acme'                                    # structured json_data filters
duvo cases search --queue <queue-id> \
  --status needs_review --priority high \
  --field '$.invoice.amount:gte:5000' --count-only                    # combine with the list filters
```

<Note>
  Reach for `search` when you need to filter by labels; keep using `list` for the
  plain status, severity, priority, date-range, structured-field, and free-text
  filters.
</Note>

### Export Cases as CSV

Download a Queue's Cases as a CSV file for reporting or spreadsheet review. The
export respects the same status, issue-severity, approval, priority, and search
filters as `duvo cases list`, plus the `--created-at-from` and
`--updated-at-from` date bounds. Export applies lower date bounds only — the `--created-at-to` and
`--updated-at-to` upper bounds are available on `duvo cases list`, not on export.
Order the rows with `--sort-by` (`created_at`, `updated_at`, or `postponed_to`;
default `created_at`) and `--sort-order` (`asc` or `desc`; default `asc`). Each
row carries the Case's id, title, status, priority, labels, lifecycle
timestamps, and data.

```bash theme={"dark"}
duvo cases export --queue <queue-id> --output disputes.csv    # write to a file
duvo cases export --queue <queue-id> > disputes.csv           # or pipe stdout
duvo cases export --queue <queue-id> --status needs_review --priority high \
  --output escalations.csv                                     # export a filtered view
```

Without `--output`, the CSV is printed to stdout so you can pipe it into another
tool.

### Manage Case labels

Attach or remove labels on a Case for filtering and organization.

```bash theme={"dark"}
duvo cases labels list <case-id> --queue <queue-id>          # list labels on a Case
duvo cases labels assign <case-id> --queue <queue-id> \
  --label "key=value" [--label "key=value" ...]              # assign one or more labels
duvo cases labels unlink <case-id> --queue <queue-id> \
  --label-id <label-id> [--label-id <label-id> ...]          # remove labels from a Case
```

<Note>
  `--label` accepts either `key=value` (for keyed labels like `priority=urgent`) or just `value` on its own (for tag-style labels like `urgent`).
</Note>

### Manage Case files

Work with the files attached to a Case — the invoice, EDI dump, or scan it was computed from.

```bash theme={"dark"}
duvo cases attachments list <case-id> --queue <queue-id>       # list the files on a Case
duvo cases attachments upload <case-id> <file> --queue <queue-id> \
  [--mime-type <type>]                                         # attach a local file
duvo cases attachments download <case-id> <attachment-id> \
  --queue <queue-id> [--output <path>]                         # save a file to disk
duvo cases attachments remove <case-id> <attachment-id> \
  --queue <queue-id>                                           # remove a file from the Case
```

<Note>
  Upload declares the file's type from its extension; pass `--mime-type` for an extension the CLI doesn't map. Files are capped at 50 MB, with 25 usable slots per Case. `--output` accepts a file path or an existing directory (the file keeps its own name inside it).
</Note>

## Queues (`duvo queues`)

```bash theme={"dark"}
duvo queues agents <queue-id>                                # list producer and consumer Agents bound to a Queue
duvo queues stats --queue-ids <id1>,<id2> \
  [--created-at-from 7d] [--created-at-to <iso-date>]        # Case status counts for up to 100 Queues in one call
```

<Note>
  `duvo queues stats` returns counts keyed by Queue ID, including zeros for Queues with no matching Cases. Queue IDs that don't belong to your team are silently dropped from the result.
</Note>

### Case-level evaluation rubrics

A Queue's case-level rubrics are the Pass/Fail questions a whole Case — across every Run that touched it — is judged against when an Agent settles it. Duvo generates them from the connected Agents' AOPs, and you can manage the set the same way as an Agent's custom rubrics: each rubric is a short title plus a Pass condition, with at most 12 per Queue version.

```bash theme={"dark"}
duvo queues eval-rubrics <queue-id>                          # list the Queue's active case-level rubrics

duvo queues eval-rubrics add <queue-id> \
  --title "Refund amount correct" \
  --description "Was the refund issued at exactly the amount the customer was owed?"
# add one rubric — fails once the Queue's current version holds 12

duvo queues eval-rubrics update <queue-id> <rubric-id> \
  --title "New title"
# edit a rubric's title and/or description (pass at least one)

duvo queues eval-rubrics remove <queue-id> <rubric-id>       # remove a rubric (prompts for confirmation;
                                                            #  refuses the last one in the set)
duvo queues eval-rubrics remove <queue-id> <rubric-id> --yes # remove without prompting

# Replace the whole set (1-12 rubrics) from a JSON array of
# { "title", "description" } objects (use "-" to read from stdin, with --yes).
# This replaces every existing rubric, so it prompts for confirmation:
duvo queues eval-rubrics replace <queue-id> --input rubrics.json
duvo queues eval-rubrics replace <queue-id> --input rubrics.json --yes
```

Add `--json` to any command for machine-readable output.

<Note>
  Edits target the Queue's current version's rubric set — the one new Cases are judged against; already-judged Cases keep their original verdicts. A Queue gets its first rubric set after its first Agent-processed Case settles, so `add` and `replace` fail with a conflict before then. Clearing the whole set isn't supported — an empty set would be regenerated at the next settlement — so `replace` requires at least one rubric, and `remove` refuses to drop the last remaining one.
</Note>

### Aggregations

An aggregation turns a typed Queue's Case data into a small table of numbers instead of a list of Cases. It is a saved, Cube-style query over the fields the Queue declares — measures (count, sum, average, min, max, percentiles), optional groupings and a time bucket, plus filters — whose result is cached and recomputed on a staleness window, so a dashboard reading it stays cheap.

<Note>
  Aggregations run over a Queue whose Cases carry structured data against a declared schema. A Queue of free-text Cases has nothing to aggregate.
</Note>

```bash theme={"dark"}
duvo queues aggregations list <queue-id>                     # list a Queue's saved aggregation definitions

# Evaluate a one-off query without saving it. --definition is the Cube-style
# grammar as a JSON object: measures (required), plus optional dimensions,
# timeDimension, filters, caseFilters, order, and limit.
duvo queues aggregations evaluate <queue-id> \
  --definition '{"name":"totals","measures":[{"name":"invoices","operation":"count"},{"name":"amount","operation":"sum","path":"$.amount"}]}'

# Save a reusable, cached definition. --max-stale-age sets how long (in seconds,
# 1-604800, default 300) a cached result may be served before a read recomputes.
duvo queues aggregations create <queue-id> \
  --definition '{"name":"by_supplier","dimensions":[{"name":"supplier","path":"$.supplier"}],"measures":[{"name":"n","operation":"count"}]}' \
  --max-stale-age 900

duvo queues aggregations result <queue-id> <definition-id>   # read a saved definition's cached result
duvo queues aggregations refresh <queue-id> <definition-id>  # force a fresh recompute now
duvo queues aggregations delete <queue-id> <definition-id>   # soft-delete a definition (prompts; --yes to skip)
```

Add `--json` to any command for machine-readable output.

<Note>
  A definition pins the Queue's schema version when you create it, so its result always reflects the fields declared then; Cases created against an incompatible later schema are excluded and reported separately. Creating, refreshing, and deleting a definition need a Builder role or above — listing, reading a result, and evaluating are open to any team member.
</Note>

## Queue labels (`duvo queue-labels`)

Queue labels are reusable label definitions on a Queue. Once defined, a label can be attached to any Case in that Queue.

```bash theme={"dark"}
duvo queue-labels list --queue <queue-id>                    # list all labels for a Queue
duvo queue-labels create --queue <queue-id> \
  --value "Urgent" [--key "priority"] \
  [--color "#FF0000"]                                        # create a new Queue label
duvo queue-labels delete <label-id> --queue <queue-id> [-y]  # delete a Queue label
```

## Scripting examples

### Relabel a Case if it had runs created today

```bash theme={"dark"}
TODAY=$(date -u +%Y-%m-%d)

duvo cases runs <case-id> --json \
  | jq -r --arg today "$TODAY" \
      '.runs[] | select(.created_at | startswith($today)) | .case_id' \
  | while read CASE_ID; do
      duvo cases labels assign "$CASE_ID" \
        --queue <queue-id> \
        --label "review=$TODAY"
    done
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.