## Background

`dci` is the command-line interface for **Cloud Intelligence™**: work with your reports, budgets, anomalies, allocations, and the rest of the platform directly from your terminal — ask about them in plain English, and hand the same tool to your AI agents. It is a standalone binary that generates its commands from the Cloud Intelligence™ API, handles authentication, and renders results for humans (pivoted, readable tables) or machines (JSON, CSV, and the token-efficient TOON format).

This documentation covers:

- Installing the CLI on macOS, Windows, and Linux
- Shell completion setup
- Authentication via OAuth or API token
- Command discovery and usage patterns
- The interactive AI session and the terminal features around it
- Output formats and scripting
- A full, generated command reference (available via the sidebar)

---

## Download and install

Install the `dci` binary using one of the methods below.

### macOS (Homebrew)

```bash
brew install doitintl/dci-cli/dci
```

### Windows (WinGet)

```bash
winget install DoiT.dci
```

### Windows (Scoop)

```bash
scoop bucket add doitintl https://github.com/doitintl/dci-cli
scoop install dci
```

### Linux

Install with one command on any Linux distribution with glibc 2.34 or newer, including Ubuntu 22.04+, Debian 12+, RHEL 9+, Fedora, Google Cloud Shell, and WSL:

```bash
curl -fsSL https://raw.githubusercontent.com/doitintl/dci-cli/main/install.sh | sh
```

The script downloads the latest release for your machine (x86_64 or arm64), verifies it against the release checksums, and installs `dci` to `~/.local/bin` with shell completion for bash, zsh, and fish. It needs no `sudo`. If `~/.local/bin` isn't on your `PATH` yet, the script prints the line to add.

:::tip Google Cloud Shell

Cloud Shell keeps only your home directory when the session restarts, so use this install command there: a package installed with `dpkg` disappears with the VM, while `~/.local/bin` persists.

:::

To change what gets installed, pass options after `sh -s --`:

| Option          | What it does                                                    |
| --------------- | --------------------------------------------------------------- |
| `--version <v>` | Install a specific release instead of the latest, e.g. `2.8.0`  |
| `--dir <path>`  | Install to another directory instead of `~/.local/bin`           |
| `--system`      | Install the `.deb` or `.rpm` package system-wide (uses `sudo`)  |

```bash
curl -fsSL https://raw.githubusercontent.com/doitintl/dci-cli/main/install.sh | sh -s -- --version 2.8.0
```

Alpine and other musl-based distributions aren't supported, and neither are older releases such as Ubuntu 20.04, Debian 11, or RHEL 8; the script stops with an explanation and leaves any existing `dci` in place. Homebrew on Linux also works: `brew install doitintl/dci-cli/dci`.

### Linux packages (Debian/Ubuntu)

To install the `.deb` package yourself, download it from the [latest release](https://github.com/doitintl/dci-cli/releases/latest) (`_amd64` or `_arm64`), then install:

```bash
sudo dpkg -i dci_*_linux_amd64.deb
```

### Linux packages (RHEL/Fedora)

To install the `.rpm` package yourself, download it from the [latest release](https://github.com/doitintl/dci-cli/releases/latest) (`_amd64` or `_arm64`), then install:

```bash
sudo rpm -i dci_*_linux_amd64.rpm
```

### Direct download

Prebuilt binaries for all platforms are available on the [GitHub Releases page](https://github.com/doitintl/dci-cli/releases/latest).

### Validate the installation

```bash
dci --version
```

### Keep it up to date

Since v2.5.2, one command updates any install:

```bash
dci update
```

`dci update` detects how the CLI was installed and does the right thing: Homebrew, Scoop, and WinGet installs run the package manager's own upgrade command after a confirmation; Linux `.deb`/`.rpm` installs download the new package, verify it against the release checksums, and install it (sudo prompts interactively — declining prints the exact command to run yourself); standalone binaries, including installs from the Linux install command, replace themselves atomically, also checksum-verified. `dci upgrade` is an alias.

| Flag               | Description                                                                                 |
| ------------------ | -------------------------------------------------------------------------------------------- |
| `--check`          | Only check for a new release and print how to update; installs nothing                       |
| `--yes`            | Skip the confirmation prompt — required before anything installs in scripts and agent mode   |
| `--version vX.Y.Z` | Update (or roll back) to a specific release tag (standalone installs only)                    |

In scripts and agent mode the result is machine-readable JSON (`current`, `latest`, `updateAvailable`, `channel`, `instruction`, and `updated` after an install), and nothing is ever installed without `--yes`.

The CLI also checks for new releases in the background at most once every few hours and prints a short notice when one is available; set `DCI_NO_UPDATE_CHECK=1` to turn that off.

---

## Shell completion

Since v2.1, Homebrew and the Linux `.deb`/`.rpm` packages install shell completion automatically, and so does the [Linux install command](#linux) (for zsh, it prints the one `fpath` line to add to `~/.zshrc`). Scoop prints the one PowerShell profile line to add after install. For other setups, generate the script for your shell below.

### Bash

```bash
# System-wide (requires sudo):
dci completion bash > /etc/bash_completion.d/dci

# Per-user:
mkdir -p ~/.bash_completions
dci completion bash > ~/.bash_completions/dci
echo 'source ~/.bash_completions/dci' >> ~/.bashrc
```

### Zsh

```bash
dci completion zsh > "${fpath[1]}/_dci"
```

Then restart your shell or run `exec zsh` to pick up the new completions.

### Fish

```bash
dci completion fish > ~/.config/fish/completions/dci.fish
```

### PowerShell

```powershell
dci completion powershell > dci.ps1
# Then add `. /path/to/dci.ps1` to your PowerShell profile.
```

Completion covers all API commands (`list-budgets`, `list-reports`, `query`, etc.) with descriptions, flag completion for each command, and — since v2.1 — your resource names: `dci get-report Mon<TAB>` offers matching report names, served from a local cache that refreshes in the background. Type names bare when completing (the shell escapes spaces for you); completing inside an open quote is not supported by shell completion frameworks. API command completion requires an active auth session (`dci login` or `DCI_API_KEY`). A Tab press never makes a network call or starts a login: on a cold cache the CLI prints a short notice instead of names and fills the cache in the background (suppress notices with `DCI_ACTIVE_HELP=0`).

---

## Authentication

The CLI supports two authentication methods.

### Interactive (OAuth)

Run `dci login` to authenticate via the console in your browser:

```bash
dci login
```

This opens a browser window, prompts you to log in, completes an OAuth flow, and caches credentials locally. In an interactive terminal, the first command you run triggers this flow automatically; non-interactive sessions (CI, pipes, agents) never wait for a browser — without credentials they fail fast with `AUTHENTICATION_REQUIRED` and a pointer to `DCI_API_KEY`. Since v2.7.0, a CI system that allocates a terminal is recognized through the standard `CI` environment variable and treated as non-interactive too.

Since v2.5.2, the browser confirmation page suggests a next command after a successful sign-in — clicking the suggestion runs it directly in the terminal that started the login (`dci login` waits up to a minute for the click; Ctrl-C skips the wait). If the sign-in fails, the page shows the error with links back to this guide.

To clear cached credentials:

```bash
dci logout
```

You can also sign in and out from inside the [AI session](#ai-session) with `/login` and `/logout`.

### API token (CI/automation)

For non-interactive environments such as CI/CD pipelines, cron jobs, and shell scripts, set the `DCI_API_KEY` environment variable to a DoiT API token:

```bash

```

When `DCI_API_KEY` is set, the CLI uses it instead of OAuth.

#### Choose a token type

DoiT supports two API token types for programmatic access. For CLI automation, choose based on whether the job runs as you or as shared organizational automation:

| Token type                    | Best for                                                                       | Where to create it                                                                                                                                                                                                                                                                                                                                                      |
| :---------------------------- | :----------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Personal API token**        | Scripts or jobs tied to your user                                              | <ol><li>Sign in to the DoiT console.</li><li>Select your avatar in the upper-right corner of the top navigation bar, and then select your name from the drop-down menu.</li><li>Select the **API** tab.</li><li>Select **Create API token**.</li></ol>                                                                                  |
| **Service account API token** | Shared automation (CI/CD, scheduled jobs) that should not depend on one person | <ol><li>Sign in to the DoiT console, select the gear icon () from the top navigation bar, and then select **Users and access**.</li><li>Select **Service accounts** from the left-hand menu.</li><li>Select a service account.</li><li>Select the **API tokens** tab.</li><li>Select **Create API token**.</li></ol> |

Use a **personal API token** for local scripts or one-off automation you run yourself. Use a **service account API token** for pipelines and scheduled jobs that must keep working if you change roles or leave the organization.

When you create a personal API token, choose a **permission scope** that matches what the CLI commands need:

| Scope            | Use when                                                  |
| :--------------- | :-------------------------------------------------------- |
| **Read only**    | The job only reads data (for example, `dci list-reports`) |
| **Full access**  | The job needs every permission your user role grants      |
| **Custom scope** | The job needs a specific subset of permissions            |

Service account tokens inherit the permissions assigned to the service account. Grant only the permissions each workload needs.

:::note

New tokens may take up to one minute to work after creation.

:::

:::caution

Treat API tokens like passwords. Store `DCI_API_KEY` in your secrets manager or CI/CD secret store. Do not commit tokens to source control.

:::

For more on creating tokens and permission behavior, see [DoiT Developer Hub: Get started](https://developer.doit.com/docs/start), [Personal API tokens](https://help.doit.com/docs/general/profile/api-tokens), and [Service accounts](https://help.doit.com/docs/user-management/service-accounts).

### Verify authentication

Check your configuration, session state, and active customer context:

```bash
dci status
```

To confirm your identity and permissions against the API, run:

```bash
dci validate
```

---

## Getting started

After installing and authenticating, try a few commands:

```bash
dci list-budgets
dci list-reports
dci get-report <report-id>
```

Use `--help` on any command for inline usage details; help is terse by default, and `--help-full` adds the complete request/response schemas:

```bash
dci --help
dci list-budgets --help
dci query --help-full
```

Since v2.7.3, command help describes each positional argument and shows curated usage examples that are checked against the live API before every release, so a command line printed in help is one the API accepts. The anomaly commands and the local commands (`dci status`, `dci query`, `dci open`, `dci update`, `dci skill`, and others) were covered first; the rest keep their previous generated help until their examples land. Command categories carry a one-line description, and a flag with a documented example shows it.

If you mistype a command name, the CLI exits with a non-zero code and suggests the closest match — and, at a terminal, the plain-English alternative `dci ai "…"` alongside it. To jump from the terminal into the console, `dci open` (or `dci open report <id>`, `budget`, `allocation`) opens the deep link in your browser — or prints it in agent and non-interactive modes.

Two commands answer a common question directly, instead of making you rebuild the filter and sort each time (v2.7.2):

```bash
dci budgets-at-risk                 # budgets currently projected to breach
dci anomalies-recent                # anomalies that appeared recently
dci anomalies-recent --window 7d    # widen the lookback from the 24-hour default
```

`dci budgets-at-risk` is the curated equivalent of `dci list-budgets --filter riskStatus:atRisk`, in projected-breach order, and `dci list-budgets` itself color-grades its utilization bars — green through amber past 70% into red past 90% — so an at-risk budget stands out in the table. Since v2.7.6, `--window` accepts hours, days, and weeks (`168h`, `7d`, `1.5d`, `2w`); a mixed form such as `1d12h`, a zero, a negative, or an oversized window is rejected with a message listing the accepted forms.

For a one-page list of the commands worth knowing, see the [CLI cheat sheet](https://help.doit.com/docs/cli/cheatsheet).

---

## AI session

Since v2.6.0, `dci ai` opens an interactive AI session in your terminal: ask about your cloud costs in plain English, and the AI runs `dci` commands for you and explains the results. You can also run any command yourself inside the session by prefixing it with `/`, with completion and history. One-shot mode answers a single question and exits:

```bash
dci ai                                              # interactive session
dci ai "top 3 cost anomalies this month"            # one-shot: answer, then exit
dci ai --quiet "what did we spend on S3 last month" # the answer only
dci ai --verbose "..." > investigation.md           # keep the narration when piped
```

`--quiet` prints just the answer; `--verbose` keeps the full narration even when output is piped to a file. The shorthand for `--quiet` is not `-q` — that shorthand belongs to another flag and is not used here.

Since v2.7.0, running `dci` with no arguments at a terminal opens the same session. In pipes, scripts, and CI, bare `dci` prints the help screen exactly as before, so nothing changes for automation. If you prefer the help screen at your own terminal, run `/default help` once inside the session; `dci --help` always prints help.

:::note

The AI session is not [agent mode](#agent-mode), despite the similar names. The session is a person-facing program that drives `dci` on your behalf in your terminal. Agent mode is the reverse: it strips the CLI of everything interactive so an external AI harness can drive `dci` and parse its output. A harness never opens the session, and the session is never in agent mode.

:::

### Slash commands and keys

Inside the session, `/` runs a `dci` command (`/list-budgets`, `/beta run-report`) and the following built-ins control the session itself:

| Command                   | Description                                                                                                                                   |
| :------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------- |
| `/help`                   | The slash-command grammar and the commands available. Your first-ever session opens with the full text; later sessions show the compact banner |
| `/key`                    | Show which Anthropic API key is in use (masked) and where it comes from, including when `ANTHROPIC_API_KEY` overrides a saved key             |
| `/key set`, `/key clear`  | Replace the saved key through the guided entry, or remove it                                                                                  |
| `/customer`               | Show or switch the customer context. A switch the AI makes itself applies to the session only — your saved context is never changed           |
| `/model`                  | Pick the AI model                                                                                                                             |
| `/export`                 | Save the transcript to a file                                                                                                                 |
| `/login`, `/logout`       | Sign in or out without leaving the session; commands, role, and tenant refresh live                                                           |
| `/bell`                   | Turn off the terminal bell that rings when an answer that ran commands finishes                                                               |
| `/mouse`                  | Toggle mouse reporting; the choice is remembered across sessions                                                                              |
| `/beta`                   | List the [early-access commands](#beta-commands) the way you would type them in the session                                                   |
| `/default help`           | Make bare `dci` print the help screen at your terminal again instead of opening the session                                                   |
| `/exit`, `/quit`          | Leave the session                                                                                                                             |

`Esc` cancels a running command or a streaming answer. `Ctrl+L` repaints the frame and jumps back to the latest content, which is the key to press when terminal scrolling has disturbed the view. In the command popup, `Enter` or `Tab` selects the highlighted command and `↓` scrolls through every match. You can also define your own shortcuts as saved commands.

When an answer runs long, the session rings the terminal bell and — in terminals that support notifications and focus reporting — sends a desktop notification, so you can switch away during an investigation.

### Reading the answer

Since v2.7.6, a one-shot `dci ai "question"` renders its answer as formatted text at a terminal: tables with borders, bold, and inline code, with narration emitted before a lookup shown as its own paragraph while the lookup runs. Piped output, agent mode, `NO_COLOR`, and an explicit `--output` format still get the raw stream, so nothing that parses the output changes. With `--quiet`, a brief `thinking…` status on stderr shows the session is working until the answer prints.

Answers label their numbers: cost tables state the currency and usage tables name the metric, with one consistent scale per column. The session batches independent commands and runs them concurrently, and presents a long result as a top-10 table plus an aggregated total — ask for the full breakdown when you want every row.

### Credentials and privacy

AI features run on **your own Anthropic API key**. The session walks you through saving one securely the first time, and `ANTHROPIC_API_KEY` in the environment overrides a saved key. Your questions and the command results the AI reads are sent to Anthropic under that key, and conversations are not stored anywhere but your terminal.

DoiT employees do not need a key: once signed in with `dci login`, the session runs on DoiT-provided access — short-lived tokens fetched with your existing DoiT sign-in, refreshed behind the scenes, never written to disk (v2.7.2). An explicit key still takes precedence, and `DCI_AI_PROVIDED=off` opts out of the provided access.

### Tuning answers

| Variable        | Description                                                                                                                   |
| :-------------- | :----------------------------------------------------------------------------------------------------------------------------- |
| `DCI_AI_EFFORT` | Trade answer speed against depth: `low`, `medium`, `high`, or `default` to restore uncapped reasoning. The default is `medium` |
| `DCI_AI_STATS`  | `1` prints one telemetry line per answer — rounds, tool calls, tokens, timing — for scripting and benchmarking                |

### Authoring flows from the session

The bundled agent skill teaches [CloudFlow](#cloudflow) authoring and, since v2.7.2, flow-_behavior_ verification: build or refine a flow from a natural-language request, or author a bundle by hand through the export, edit, dry-run, import loop, then deep-validate the draft with `dci test-run-cloudflow-flow --dry-run`, run it once as a test, and read what every node consumed and produced with `dci get-cloudflow-flow-run`. Run `dci skill update` to refresh installed copies — see [Agent mode](#agent-mode).

---

## Command structure

Commands are generated directly from the Cloud Intelligence™ API. The general structure is:

```bash
dci <command> [arguments] [flags]
```

Examples:

```bash
dci list-alerts
dci get-alert <id>
dci list-budgets
dci list-reports
```

For commands that accept a request body, pipe a JSON file via stdin or pass fields as inline shorthand arguments:

```bash
dci create-alert < alert.json
dci invite-user email: user@example.com, roleId: <role-id>
```

Inline shorthand treats commas structurally, so free text containing a comma has to go through stdin as JSON.

A body field that takes a **file** — `dci ingest-datahub-events-csv`'s `file` — is given its path prefixed with `@` in the same shorthand. The file is attached by name, and a missing file, an oversized file, or a forgotten `@` is reported with a fix before anything is sent:

```bash
dci ingest-datahub-events-csv provider: litellm-usage, file: @events.csv
```

The CLI validates request bodies against the API schema: unknown top-level fields are rejected with a usage error listing the valid fields, instead of being silently ignored by the API.

---

## Resource names

Since v2.1, names work wherever IDs do: `dci get-report "Monthly AWS Spend"` resolves the name to its ID before the request goes out, and multi-word names work unquoted. Names match exactly, by unique substring, or by close typo. Tab completion offers your resource names from a local cache that refreshes in the background.

```bash
dci get-report "Monthly AWS Spend"   # name instead of ID
dci get-budget Team Backyard         # quoting optional
dci get-report --id <value>          # force a literal ID, skip the lookup
dci get-report --name <value>        # force a name lookup for an ID-shaped name
DCI_NO_RESOLVE=1 dci get-report <id> # scripts: treat arguments as literal IDs
```

When a name matches more than one resource, an interactive terminal offers the matches as a menu; scripts get every match with its ID and a clean error, so nothing ever runs on a guess.

Since v2.7.5, resolution and Tab completion also reach commands that act on a sub-resource — `dci get-report-config`, `dci export-cloudflow-flow`, and their siblings take a name where they previously took only an ID — and DataHub dataset names, which have no separate ID and so resolved nowhere before. Commands whose identifier is a number, such as `dci get-ticket`, are excluded: they no longer offer names they cannot accept.

---

## Interactive terminal features

In a terminal, the CLI wraps the same commands in prompts, pickers, and viewers (v2.5.1 and later). None of this appears in pipes, scripts, CI, or [agent mode](#agent-mode), which get exactly the output they got before — that is what makes one command line safe in both places. Since v2.7.0, a CI system that allocates a terminal is detected through the standard `CI` environment variable and gets the script behavior, so a pipeline can never hang waiting on a prompt. `DCI_NO_TUI=1` switches all of it off in a terminal too, keeping tables and color.

What the terminal adds:

- **Resource picker**: leave the identifier off a command that needs one — `dci get-report`, `dci open report`, `dci export-datahub-dataset-records` — and a filter-as-you-type list opens instead of a usage error.

- **Selection menu**: a name that matches more than one resource is offered as a list to choose from, described under [Resource names](#resource-names).

- **Destructive confirmation**: deletes and similar commands show the target's name, owner, and description before anything happens. Cancel is the default, and `Esc` cancels too.

- **Full-screen table viewer**: `-M interactive` browses a result with `←→` for columns, `s` to sort, `/` to filter, and `Enter` to print the highlighted row's id.

- **Query builder**: `dci query` with no input composes a report query step by step, then runs it or saves it as a `query.json`.

- **Spinner**: shown while an API request is in flight.

- **Hidden-columns hint**: one compact line listing the columns that did not fit the current table.

- **Scroll hint**: when output is taller than the terminal, a dim note next to the prompt says how much is above it ("↑ N more lines above — scroll up", v2.7.2).

The [AI session](#ai-session) is the largest of these terminal-only features and has its own section.

---

## Running queries

`dci query` runs a Cloud Analytics report query without persisting it. The request body is a JSON object with a `config` key describing metrics, grouping, time ranges, and filters (there is no SQL input mode):

```bash
dci query < query.json
```

Always include a group `limit` in query configs — an unlimited grouped query over a long time range can return thousands of rows. Run `dci query --help-full` to see the full request schema and a complete example payload, or run `dci query` with no input to build the config interactively.

### Report results

Report and query results (`result.rows`) are positional arrays zipped with `result.schema`. The following flags shape them:

In the interactive table view, report results render as a **pivot** by default — groups as rows, time periods as columns, with totals, a first→last `trend` column, and heatmap shading by magnitude — matching how the console presents a report. Since v2.5.1 this holds for every range: the 14-period cutoff that used to switch wide results to flat rows is gone, and `--flat` is how you opt out. Machine formats (`json`, `yaml`, `csv`, `toon`) and agent mode keep the flat row layout; since v2.7.6, an explicit `-C` column selection keeps the pivot and simply narrows it to the columns you named. Pivot rows whose values are all zero are hidden, and flat rows omit datetime dimension columns that only repeat the timestamp column. Timestamps follow the report's time resolution (hourly results keep the hour on every row; daily and coarser show bare dates) and period columns are always UTC — they label billing buckets, not moments (see [Timestamps and timezones](#timestamps)).

Monetary amounts are currency-aware when the query config specifies a currency or a response row carries one (such as a budget). Human tables render those amounts with the currency sign rounded to whole units (`$239,927`, `€205,956`), and machine-formatted report results include the explicit currency. The CLI does not assume a currency when the API response and request omit it. `--raw-numbers` restores exact unformatted values. The following flags shape results further:

| Flag                    | Description                                                                                                                                                   |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--flat`                | Render report results as flat rows instead of the default pivot view                                                                                          |
| `--pivot`               | Force the pivot view in any mode or output format                                                                                                             |
| `--heatmap`             | Shade pivot cells by magnitude in interactive terminals (default on; respects `NO_COLOR`; `--heatmap=false` disables)                                         |
| `--rows keyed`          | Return rows as objects keyed by schema column names instead of positional arrays                                                                              |
| `--rollup <columns>`    | Total rows in the CLI: rows group by the listed result columns, metric columns are summed, and per-period rows collapse into one total per group (v2.6.0)     |
| `--max-rows <n>`        | Cap the number of result rows (`0` = unlimited). Agent mode defaults to `500`; when rows are omitted the result carries `rowsOmitted` and `rowsTotal` markers |
| `--include-empty-rows`  | Keep rows with a null group and zero metrics (dropped by default; `emptyRowsDropped` marks how many were removed)                                             |
| `--drop-unlabeled-rows` | Exclude report rows whose group labels are **all** null; partially labeled rows are always kept (v2.5.0)                                                      |
| `--raw-numbers`         | Print numbers unformatted in table output                                                                                                                     |
| `--chart[=<style>]`     | Render an ASCII chart of the report next to the prompt, under the table. Human table output only — ignored in machine formats and agent mode, and unaffected by `-C` |

`--chart` takes one of five styles, all of them following the report's color theme:

| Style           | What it draws                                                                                                                                                                           |
| :-------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| stacked columns | The default: one stacked column per period, largest segments on top (v2.5.2)                                                                                                            |
| `line`          | A line of period totals (v2.5.2)                                                                                                                                                        |
| `sparkline`     | Period totals on a single line — the quickest look at the shape (v2.7.0)                                                                                                                |
| `heatmap`       | One row per group and one cell per period, colored by each value's share of the maximum (v2.7.0)                                                                                        |
| `treemap`       | Each group's share of the total as proportional rectangles, with exact shares in the legend. It needs no time axis, so it also charts single-period reports the other styles cannot (v2.7.2) |

`get-report` also accepts `--time-range` (e.g. `P30D`), or `--start-date` and `--end-date` (`yyyy-mm-dd`, must be provided together) to override the report's time settings.

---

## Output formats

Commands support multiple output formats via the `--output` flag:

| Format  | Description                                                              |
| ------- | ------------------------------------------------------------------------ |
| `table` | Human-readable tabular display (default in interactive terminals)        |
| `json`  | Machine-readable JSON — recommended for scripting                        |
| `yaml`  | YAML structured output                                                   |
| `csv`   | Comma-separated values for spreadsheet import (list and report results)  |
| `toon`  | Compact, token-efficient encoding for LLM agents (default in agent mode) |
| `auto`  | Alias for the mode default                                               |

```bash
dci list-budgets --output json
dci list-budgets --output table
dci list-reports --output csv > reports.csv
```

Use `--fields id,name` to project responses to specific fields before output, and `--exclude description` to remove fields.

### Searching a list

Since v2.6.0, `--search <substring>` finds items in any list command by keyword, case-insensitively, across every text field and every page of the response. The match runs in the CLI, so it works on endpoints that offer no server-side filter:

```bash
dci list-dimensions --search genai
dci list-cloudflows --search cleanup
```

Some commands also take server-side filters — `--filter` and `--name-contains` on reports and budgets, for example. Those are per-command; see the command's page in the reference for the keys it accepts.

### Paging

A list command returns one server page by default. Since v2.5.0, `--all` follows the page tokens and merges every page before rendering, so you get the complete list without chasing tokens yourself:

```bash
dci list-reports --all --output json
```

| Flag            | Description                                                                                                                             |
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `--all`         | Fetch and merge every page. On a command that already returns everything, it notes that there was nothing more to fetch                 |
| `--page-token`  | Continue from the token a previous response returned                                                                                    |
| `--max-results` | Rows per page. A value beyond the server's cap fails up front with the real limit                                                       |

Paged lists say so when a page token is dropped, and since v2.7.5 `--page-token` and `--max-results` fail with a clear message on commands that return their whole response in one request, instead of accepting the value and ignoring it.

### Row order

Since v2.7.2, human tables land the rows you came for nearest the prompt: lists sort newest-last, insights rank top-savings-last, and report groups put the largest spenders just above the `TOTAL` row. Machine formats (`json`, `yaml`, `csv`), agent mode, and piped output keep the classic ordering, so nothing scripted changes. Set the ordering for one command with `--output-order`, for an environment with the `DCI_OUTPUT_ORDER` environment variable, or persistently with `dci config`.

### Saving output to a file

`--output-file` (`-O`) writes a command's output to a file instead of the terminal, in whatever format the command would have printed. Pass a path, or a directory — including `.` — to save under the filename the API suggests:

```bash
dci list-budgets --output csv --output-file budgets.csv
dci export-datahub-dataset-records my-dataset --all --output-file .
```

A write that fails partway removes the partial file, so a truncated download never looks like a complete one.

### Exporting data

A few commands answer with a **file** rather than a table. `dci export-datahub-dataset-records` streams the records of a [DataHub](https://help.doit.com/docs/integrations/datahub/work-with-datahub) dataset as CSV, or as newline-delimited JSON with `--format jsonl`, and the CLI prints those bytes exactly as the API sent them — `--output` does not apply to them, `--search` is rejected because there are no items to filter, and `--output-file` is how you save them.

The API returns one page per request, requires both time bounds, and accepts at most 366 days per request. `--all` handles all three: it follows the page tokens, walks the successive time windows, and merges everything into one export with a single header row. Consecutive pages can carry different label and metric columns, so the headers are unioned rather than concatenated and a value never lands under the wrong column. With no `--start-time`/`--end-time` at all it also finds the range the dataset covers and prints the range it settled on to stderr, so exporting everything is one command:

```bash
dci export-datahub-dataset-records my-dataset --all --output-file records.csv
```

A window wider than 366 days is rejected before the request, with a pointer to `--all`. Without `--all`, an export that has more rows waiting says so on stderr and prints the `--page-token` to continue from — nothing in a CSV or JSONL stream itself would reveal that the file is partial. For ordinary list commands, `--all` behaves as described under [Paging](#paging).

See [export-datahub-dataset-records](https://help.doit.com/docs/cli/generated/command-groups/datahub/export-datahub-dataset-records) for the full flag list, including `--for-reimport`, which rewrites an export so it can be loaded into another dataset with [ingest-datahub-events-csv](https://help.doit.com/docs/cli/generated/command-groups/datahub/datahub-events-csvfile).

### Table output options

Tables are rendered for reading: text aligns left and numbers right, timestamps display as human-readable dates (bare dates at report grain, minute precision for event times — shown in your [local timezone](#timestamps)), integral numbers group without decimals, and when a response has more columns than fit the terminal, the table keeps the ones that render readably — identity (`id`, `name`) and date columns first — and lists the hidden rest with a hint (`-C` to choose, `-M wrap` to wrap, `-W` to widen). `--raw-numbers` restores exact unformatted values.

The following flags control how the table is rendered:

| Flag                    | Short | Default     | Description                                                                                                                             |
| ----------------------- | ----- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `--table-mode`          | `-M`  | `fit`       | `fit` truncates long cell values to fit the terminal width. `wrap` wraps them across multiple lines. `interactive` opens the full-screen viewer. |
| `--table-columns`       | `-C`  | all columns | Comma-separated list of columns to include, in the order listed.                                                                        |
| `--table-width`         | `-W`  | auto-detect | Table width in columns. Defaults to terminal width, falls back to the `COLUMNS` env var, then `120`.                                    |
| `--table-max-col-width` | `-X`  | `0` (auto)  | Maximum width per column when fitting or wrapping. `0` lets the CLI decide automatically.                                               |

Example combining multiple options:

```bash
dci list-budgets --table-mode wrap --table-columns id,name,amount --table-max-col-width 40
```

On report output, `-C` selects pivot columns and keeps the pivot layout (v2.7.6): `dci get-report <id> -C service_description,2026-04,TOTAL` shows those columns in the order given, and `TOTAL` and `Trend` match case-insensitively. A column the pivot does not have is a usage error listing the columns that are available, and pointing to `--flat` when the name belongs to the flat layout.

Many list commands also ship a curated default view: the most useful columns lead, and ids resolve to names — a report's owner, a role's name — instead of raw identifiers. Machine formats and explicit `-C`/`--fields` selections always return the raw fields.

### Timestamps and timezones

Interactive tables show **event timestamps** — created, updated, acknowledged, and similar moments — in your local timezone. Columns carrying such values are titled with a `(local)` suffix (for example `updated (local)`), and a one-line note on stderr names the zone in use:

```
note: times shown in local time (UTC+03:00); pass --utc for UTC
```

Everything else stays in UTC by design:

- **Report period columns** (daily and hourly cost buckets) and **anomaly usage windows** (`started (UTC)`) label UTC billing buckets — shifting them would move costs onto the wrong day.
- **Calendar dates** — contract terms, invoice dates, budget periods — render as plain dates.
- **Machine formats** (`json`, `yaml`, `csv`, `toon`) and **agent mode** always emit UTC or raw epoch values, so scripted output is identical on every machine.

| Control              | Effect                                                                                    |
| -------------------- | ----------------------------------------------------------------------------------------- |
| `--utc`              | Keep table timestamps in UTC (time columns are titled `(UTC)`)                             |
| `DCI_TZ=<IANA name>` | Display in a specific timezone instead of the system one, e.g. `DCI_TZ=Europe/Berlin`      |

---

## DataHub

[DataHub](https://help.doit.com/docs/integrations/datahub/work-with-datahub) datasets hold the cost and usage events you send to Cloud Intelligence™ yourself. A dataset is addressed by its `name` — the value `dci list-datahub-datasets` shows, and the value ingest sends as `provider`. A dataset's display name is what the console renders and is never an ingest identifier. Since v2.7.5, dataset names complete on Tab, resolve from a partial name, and open the picker when you leave the name off.

### Sending events

Two commands ingest events, and both take the request body as inline `field: value` shorthand or as JSON on stdin (see [Command structure](#command-structure)):

```bash
# A CSV file, or a GZ/ZIP archive containing one
dci ingest-datahub-events-csv provider: litellm-usage, file: @events.csv
dci ingest-datahub-events-csv provider: litellm-usage, file: @events.csv.gz

# A JSON batch
dci ingest-datahub-events < events.json
```

Since v2.7.4, `dci ingest-datahub-events-csv` uploads the file named after `@`. The API's limit is 30 MB per file, so a larger export must be gzipped or zipped first, one CSV per archive, and a file over the limit, a missing file, or a forgotten `@` is reported with a fix before anything is sent. The CSV must follow the dataset's [schema template](https://help.doit.com/docs/integrations/datahub/import-data/upload-csv), and the dataset must exist — create it with `dci create-datahub-dataset name: <dataset>` first. `dci ingest-datahub-events` sends the same events as JSON, each with a `provider`, an RFC 3339 `time`, and its dimensions and metrics.

Ingested rows take up to 15 minutes to appear in the DoiT console and in Cloud Analytics reports. There is no delete-by-batch API, so to undo a bad upload, remove the rows with `dci delete-datahub-events-by-filter` over the time range the file covered, then upload the corrected file.

### Copying between datasets

`--for-reimport` rewrites an export into the vocabulary `ingest-datahub-events-csv` accepts — dropping the export-only `batch`, `source`, `export_time`, and `updated_by` columns and renaming `event_id` to `id` — so records can be moved from one dataset to another:

```bash
dci export-datahub-dataset-records old-dataset --all --for-reimport --output-file records.csv
dci ingest-datahub-events-csv provider: new-dataset, file: @records.csv
```

See [Exporting data](#exporting-data) for how `--all` covers pages and time windows, and the [DataHub command group](https://help.doit.com/docs/cli/generated/command-groups/datahub/) for every flag.

---

## CloudFlow

The CLI drives [CloudFlow](https://help.doit.com/docs/operate/cloudflow/intro) end to end: build a flow, export it, import it elsewhere, test it, run it, and read back what each node did. Flows are addressed by the `id` that `dci list-cloudflows` shows first. The API does not filter this listing server-side, so narrow it with `--search`:

```bash
dci list-cloudflows
dci list-cloudflows --search cleanup
```

### Authoring

`dci build-cloud-flow` creates a new flow and generates its nodes and connections from a natural-language intent, and `dci refine-cloud-flow <flow-id>` changes an existing one. Since v2.7.2 both answer with a readable result — the flow's ID, the `conversationId` to pass back for the next refinement, the builder's reply, and the build steps that ran — instead of the raw event stream, and both accept the family-consistent spellings `dci build-cloudflow` and `dci refine-cloudflow`:

```bash
dci build-cloud-flow < build.json
dci refine-cloud-flow <flow-id> question: "Add an approval step before the node that deletes disks"
```

Free text containing a comma must go through stdin, because inline shorthand treats commas structurally. Builder output lands as an unpublished draft in your own tenant, so export it and read the nodes before trusting it, and re-export after a refine to confirm the change was saved.

### Moving flows between tenants

`dci export-cloudflow-flow <flow-id>` serializes the flow, plus every flow it references through subflow nodes, into a tenant-neutral, credential-free JSON bundle. Connections, Datastore tables, and global variables travel as named requirements to be rebound at import; credentials, tenant identifiers, and execution state never appear in the bundle. The command prints JSON by default, so redirecting it to a file is safe.

`dci import-cloudflow-flow` creates every flow of a bundle in the authenticated tenant. Imports are create-only: each call creates new draft flows with new IDs, nothing is published, and no schedule is activated. A real import requires an idempotency key up front, while `--dry-run` is the API's own dry run and returns the import plan without writing:

```bash
dci export-cloudflow-flow <flow-id> > bundle.json
dci import-cloudflow-flow --dry-run < bundle.json
dci import-cloudflow-flow --idempotency-key "$(uuidgen)" < bundle.json
```

### Running and diagnosing

| Command                         | What it does                                                                                                                                     |
| :------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------- |
| `dci test-run-cloudflow-flow`   | Runs a flow once as a test and accepts a draft. `--dry-run` deep-validates it with the same checks publishing applies, without starting a run    |
| `dci trigger-cloudflow-flow`    | Starts a run of a published flow; a draft is rejected with `422`                                                                                 |
| `dci trigger-cloudflow-webhook` | Triggers a published flow whose first node is a webhook trigger, passing the request body through as the webhook payload                         |
| `dci stop-cloudflow-flow`       | Stops the run in progress — no run ID needed, since a flow can only have one active run                                                          |
| `dci list-cloudflow-flow-runs`  | A flow's runs, newest first. `--mode test` or `--mode standard` separates test runs from production ones                                         |
| `dci get-cloudflow-flow-run`    | One run's status and, per node, the JSON it consumed and produced — how you find out why a run failed, or that it "succeeded" with the wrong data |

A test run executes for real: the same graph, the same connections, the same credentials, and the same approval behavior — an approval-gated node still parks the run, and no request skips one. It is recorded as a test, so it stays out of run history and dashboard statistics, but it does hold the flow's concurrency lock, so a flow that is already running answers `409`.

`dci get-cloudflow-flow-run` is the diagnosis tool. `input` is recorded only by action nodes — the cloud and DoiT operations — and is `null` for transform, code, branch, switch, datastore, subflow, and trigger nodes by design, while `output` is recorded by every node that finishes, so read a transform's behavior from its output. Payloads appear as soon as a node reaches a terminal status, sensitive values are redacted, and each payload is capped at 64 KB with `truncated` and `totalBytes` marking what was cut. Since v2.7.2 a failed run prints in full — status, per-node results, and the failure message — instead of collapsing into an error envelope.

`dci trigger-cloudflow-webhook` runs real automation and is classified as [destructive](#destructive-commands): terminals confirm, scripts pass `--yes`, and `--dry-run` prints a local preview.

---

## Beta commands

`dci beta` is the early-access command surface, introduced in v2.7.0. Beta commands use your existing sign-in, customer context, and [output formats](#output-formats), and idempotency keys are generated for you unless you pass your own. `dci commands --beta` lists the surface, and an account not yet enrolled in the early-access program gets a clear hint rather than a bare error.

The surface starts with asynchronous report execution:

```bash
dci beta run-report                 # run a saved report asynchronously
dci beta run-report-config          # run an inline report configuration
dci beta get-report-operation       # poll the operation
dci beta get-report-results         # read the rows once it succeeds
dci beta cancel-report-operation    # cancel a pending or running operation
```

Since v2.7.2, `dci beta run-report` resolves report names like the rest of the CLI: a typed name resolves to its ID, multi-word names work unquoted, and no argument at all opens the report picker, in the shell and in the [AI session](#ai-session) alike. Inside the session, beta commands complete in the popup and run like any other `/` command, and a bare `/beta` lists them.

---

## Agent mode

The CLI auto-detects when it is driven by an AI agent (via common agent environment variables) and switches to agent mode: output defaults to compact TOON, colors and decorations are disabled, and informational messages are routed to stderr so stdout stays parseable. Force it on with `--agent` or `DCI_AGENT_MODE=1`, or off with `--no-agent`; `dci status` shows the current mode. Agent mode is not the [AI session](#ai-session): it is the mode an external harness drives the CLI in, and it never shows a prompt, picker, or viewer.

In agent mode, failures are written to stderr as a JSON envelope with a stable `code`, `message`, and `retryable` flag, plus optional `hint`, `http_status`, `request_id`, and `retry_after` fields:

```json
{
  "error": {
    "code": "RESOURCE_NOT_FOUND",
    "message": "Not Found",
    "retryable": false,
    "http_status": 404,
    "request_id": "..."
  }
}
```

Agent mode also caps report results at 500 rows by default (see [Report results](#report-results)) and fails fast with `AUTHENTICATION_REQUIRED` when no credentials are available, instead of starting a browser login flow.

To install the CLI's agent skill (usage guidance embedded in the binary) into a coding agent's configuration directory:

```bash
dci skill claude    # also: codex, cursor, gemini, kiro, opencode
dci skill list      # inspect embedded files and token estimates
dci skill update    # refresh installed copies after a CLI update
```

The bundled skill carries more than command syntax: it teaches addressing resources by [name](#resource-names) rather than id, and, since v2.7.2, [CloudFlow](#cloudflow) authoring and flow-behavior verification — clone real flows instead of guessing node parameters, validate with a dry run, land changes as drafts, and read a test run's per-node payloads before reporting a flow as working. A skill installed from an older release does not have these; `dci skill update` refreshes it.

For machine-readable command metadata — arguments, flags, output shapes, and destructive-operation classification — use:

```bash
dci commands --json
dci commands --beta
```

Since v2.7.3, `dci commands --json` also carries each command's curated examples, page notes, and related commands, so an agent can read them without parsing help text.

### Documentation for agents

Agents can discover and consume the CLI documentation without a browser:

- `dci docs` prints this page's URL and the other documentation entry points from the terminal
- Every page of this Help Center is available as plain Markdown by appending `.md` to its URL — this page is [help.doit.com/docs/cli.md](https://help.doit.com/docs/cli.md)
- [help.doit.com/llms.txt](https://help.doit.com/llms.txt) is a machine-readable index of all Help Center pages (including the CLI command reference), and [llms-full.txt](https://help.doit.com/llms-full.txt) inlines the full content
- [help.doit.com/llms-cli.txt](https://help.doit.com/llms-cli.txt) is a compact CLI-only index — the guides plus every command, each linked to its Markdown page — for agents that only need to drive `dci`
- `dci skill <agent>` installs task-oriented usage guidance directly into the agent's configuration, `dci skill update` refreshes an installed copy, and `dci <command> --help-full` includes the full request/response schema for every command

For agents working with DoiT data through tool calls rather than a CLI, see the [MCP server](https://help.doit.com/docs/mcp/connections).

---

## Customer context

DoiT employees and multi-tenant users must select which customer their commands operate on. Set it temporarily with the `DCI_CUSTOMER_CONTEXT` environment variable or the `-D/--customer-context` flag, or persist a default. The value can be a customer domain (`acme.com`), a customer ID, or the customer's URL display name as it appears in DoiT Console URLs (`console.doit.com/customers/acme/…`):

```bash
DCI_CUSTOMER_CONTEXT=acme.com dci list-budgets   # this command only
dci list-budgets -D acme.com                     # this command only
dci customer-context set acme.com                # persistent default
dci customer-context set acme                    # URL display name works too
dci customer-context show
dci customer-context clear
```

A wrong persisted context makes every command fail with `PERMISSION_DENIED`; the error hint names the active context. Verify access after switching with `dci validate`.

In the [AI session](#ai-session), `/customer` shows or switches the context for that session only, and signing in as a different account drops the previous account's context and cached names.

---

## Destructive commands

Commands classified as destructive (deletes, flow triggers, and similar) refuse to run without confirmation and exit with code `30`:

```bash
dci delete-report <id>            # refuses: requires confirmation
dci delete-report <id> --dry-run  # prints a local preview without executing
dci delete-report <id> --yes      # executes
```

In an interactive terminal the refusal is a confirmation dialog instead: it names the resolved target — name, owner, and description — Cancel is the default, and `Esc` cancels it.

Set `DCI_CONFIRM_DESTRUCTIVE=1` to confirm globally in automation that has been reviewed — do not set it as a blanket default.

---

## Environment variables

| Variable                   | Description                                                                                                          |
| -------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `DCI_API_KEY`              | API token for non-interactive authentication (personal or service account)                                           |
| `DCI_CUSTOMER_CONTEXT`     | Override the customer context for commands                                                                           |
| `DCI_AGENT_MODE`           | Force agent mode on (`1`) or off (`0`)                                                                               |
| `DCI_CONFIRM_DESTRUCTIVE`  | Confirm destructive commands without `--yes` (use with care)                                                         |
| `DCI_SKIP_BODY_VALIDATION` | Bypass client-side request-body validation (e.g. when a brand-new API field is rejected against a stale cached spec) |
| `DCI_NO_TUI`               | Disable interactive pickers, prompts, and viewers in a terminal (tables and color are kept) — see [Interactive terminal features](#interactive-terminal-features) |
| `DCI_NO_RESOLVE`           | Disable name resolution and pickers — arguments are treated as literal IDs                                           |
| `DCI_NO_UPDATE_CHECK`      | Disable the background release check and its update notice                                                           |
| `DCI_ACTIVE_HELP`          | `0` suppresses the short notices shell completion prints, such as the cold-cache notice                              |
| `DCI_OUTPUT_ORDER`         | Row order for human tables — see [Row order](#row-order)                                                             |
| `DCI_API_BASE_URL`         | API base for this invocation only; it is never persisted into your saved configuration                               |
| `ANTHROPIC_API_KEY`        | Anthropic API key for the [AI session](#ai-session); overrides a key saved with `/key set`                           |
| `DCI_AI_EFFORT`            | AI answer effort: `low`, `medium` (the default), `high`, or `default` for uncapped reasoning                         |
| `DCI_AI_STATS`             | `1` prints one telemetry line per AI answer (rounds, tool calls, tokens, timing)                                     |
| `DCI_AI_PROVIDED`          | `off` opts a DoiT employee out of DoiT-provided AI access, falling back to an explicit key                           |
| `NO_COLOR`                 | Disable colored output, including the pivot heatmap and charts                                                       |
| `DCI_TZ`                   | Timezone (IANA name, e.g. `Europe/Berlin`) for table timestamps — see [Timestamps and timezones](#timestamps)        |

:::note

`CI` is read, not set by you. Since v2.7.0 the CLI treats the standard `CI` environment variable as a signal that it is running in a pipeline, and uses the script behavior even when the runner allocates a terminal.

:::

---

## Configuration

Configuration is stored in the OS-specific config directory and is created automatically on first run.

| OS      | Path                                          |
| ------- | --------------------------------------------- |
| macOS   | `~/Library/Application Support/dci/apis.json` |
| Linux   | `~/.config/dci/apis.json`                     |
| Windows | `%APPDATA%\dci\apis.json`                     |

To reset configuration, delete the file at the path above and re-run `dci login`.

---

## Using the CLI in scripts

The CLI is suitable for shell scripts, cron jobs, and CI/CD pipelines.

Recommendations:

- Use `--output json` for machine-readable output
- Set `DCI_API_KEY` to a personal or service account API token for non-interactive authentication; without credentials, non-interactive invocations fail fast instead of waiting for a browser login
- Branch on exit codes rather than parsing error text
- Pass `--all` when you need every page, and `--yes` before any destructive command
- Nothing interactive runs in a pipe, a script, or CI, so no script can hang on a prompt; `DCI_NO_RESOLVE=1` additionally treats every argument as a literal ID

Example:

```bash

budgets=$(dci list-budgets --output json)
```

### Exit codes

Exit codes are stable across interactive, script, and agent usage:

| Code | Meaning                                                             |
| ---- | ------------------------------------------------------------------- |
| `0`  | Success                                                             |
| `1`  | Generic failure                                                     |
| `2`  | Usage error (unknown command, flag, or request body field)          |
| `10` | Authentication required or failed                                   |
| `11` | Permission denied (check the customer context)                      |
| `20` | Resource not found                                                  |
| `21` | Conflict                                                            |
| `30` | Validation error, or a destructive command run without confirmation |
| `40` | Server or upstream error                                            |
| `41` | Network error                                                       |
| `50` | Rate limited                                                        |

---

## Next steps

- Browse the **CLI command reference** in the sidebar, or the [cheat sheet](https://help.doit.com/docs/cli/cheatsheet) for the one-page version
- Use `--help` on any command for inline usage details and tested examples
- Ask a question in plain English with `dci ai` — see [AI session](#ai-session)
- Integrate the CLI into scripts and automation using `DCI_API_KEY` and an API token from the console
- Install the agent skill with `dci skill <agent>` if you drive the CLI from an AI coding assistant, and keep it current with `dci skill update`
