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

# AI agent workflow

> Use Claude (or another agent) in your terminal to translate natural-language intent into structured upstack CLI calls.

The CLI is intentionally dumb — Claude (or another agent) running in your terminal does the natural-language parsing and constructs structured `upstack` calls. No LLM ever runs server-side in Upstack.

## Example session

```text theme={null}
> Claude, make me a dashboard with NC ROAS, MER, and Facebook CPM.

$ upstack measures --search "new customer roas"   # confirms core.new_customer_roas
$ upstack measures --search mer                   # core.new_customer_mer
$ upstack measures --search "facebook cpm"        # meta.cpm
$ upstack dashboard view build \
    --name "NC + Meta Pulse" \
    --measures core.new_customer_roas,core.new_customer_mer,meta.cpm
# → Created dashboard view 5f1e6a4f-... "NC + Meta Pulse" with 3 widget(s).
#   Open the dashboard app and select it from the view dropdown.
```

The agent picks measure ids using [`upstack measures`](/cli/measures), then issues a single [`build`](/cli/dashboard-view#build) call. Same result as the [API quickstart](/api-reference/overview), in one structured command.

## Filters: discover before you construct

When the user asks for a scoped query — "MER for new customers only", "spend on Meta campaigns containing 'spring'", etc. — the agent should call [`upstack filters`](/cli/filters) first to enumerate valid filter fields, operators, and contexts before guessing field ids:

```text theme={null}
> Claude, show me MER for new customers only, last 30 days.

$ upstack filters --search customer --detail   # → orders.customer_type, operators include `equals`/`in`
$ upstack query \
    --measures core.new_customer_mer \
    --granularity day \
    --date-start 2026-04-01 --date-end 2026-04-30 \
    --filter '{"and":[{"field":"orders.customer_type","op":"equals","value":"new_customer"}]}'
```

The field id (`orders.customer_type`), its supported operators, and the set of applicable contexts all come from the discovery endpoint — no hard-coding.

## Why this works

* **Discovery is free.** [`upstack measures`](/cli/measures) and [`upstack filters`](/cli/filters) require only `analytics:read`. An agent can introspect what's available before constructing a request.
* **The CLI is structured input for the agent's structured output.** Flags are JSON-shaped (`--measures m1,m2,m3`, `--from-file payload.json`). No shell-quoting gymnastics for natural-language prompts.
* **Every command can be replayed.** Pass `--from-file` to load a JSON body, then iterate via flags. The agent doesn't need to remember state — it can re-issue.

## What the agent should NOT do

* Don't ask the user for their API key over chat. Credentials are already in `~/.upstackrc`; the CLI handles auth automatically.
* Don't hand-construct API request bodies in chat. Use the CLI as the structured interface — it's already typed and validated.
