# Lwql Charts

> Author a saved analytics chart from a plain question and place it on a dashboard. Discovers the LangWatchQL analytics schema, writes and test-runs the SQL, saves it as a chart with a Vega-Lite specification, and places it where the team already looks. Use when asked to build, save, run, or dashboard a metric or chart.

- Skill: `langwatch/lwql-charts-3` (Agent Skill)
- Install (CLI): `npx skillmds@latest add langwatch/lwql-charts-3`
- Raw SKILL.md: https://api.skillmd.com/api/skills/langwatch/lwql-charts-3/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Data & Analytics
- License: MIT
- Author: langwatch (https://skillmd.com/u/langwatch)
- Updated: 2026-09-21
- Page: https://skillmd.com/skills/langwatch/lwql-charts-3

---


# Author a Chart and Place It on a Dashboard

Turn a question ("how many traces per day?", "cost by model this week") into a saved chart that keeps updating on a dashboard. The loop is: **discover the schema → write and test-run the SQL → save the chart → place it**.

## Prerequisites

Use `langwatch docs <path>` to read documentation as Markdown. Some useful entry points:

```bash
langwatch docs                                    # Docs index
langwatch docs integration/python/guide           # Python integration
langwatch docs integration/typescript/guide       # TypeScript integration
langwatch docs prompt-management/cli              # Prompts CLI
langwatch scenario-docs                           # Scenario docs index
```

Discover commands with `langwatch --help` and `langwatch <subcommand> --help`. List and get commands accept `--format json` for machine-readable output. Every list command takes `--limit <n>` to cap the rows and `--jq <expr>` to read part of the answer. A paginated list answers with an envelope, so count its rows through the row array (`--jq '.traces | length'`), and read how many there are in all at `.pagination.total`. Bare `--jq length` counts the fields of the envelope, not the rows. Read the docs first instead of guessing SDK APIs or CLI flags.

If no shell is available, fetch the same Markdown over plain HTTP. Append `.md` to any docs path (e.g. https://langwatch.ai/docs/integration/python/guide.md). Index: https://langwatch.ai/docs/llms.txt. Scenario index: https://langwatch.ai/scenario/llms.txt

If anything fails or confuses you while following this skill (broken commands, docs that do not match reality, errors you had to work around), ask the user for permission and run `npx --yes langwatch report --user-approved` with a `--title` and `--summary` (or `--session <transcript.jsonl>`) to send it to the LangWatch team, and it directly shapes what gets fixed. No login or API key needed. Nothing is sent without `--user-approved`, and `--dry-run` prints the exact payload without sending anything. The title, summary and transcript are scrubbed locally first, by pattern: secrets and API keys, plus email addresses, phone numbers, card numbers and public IPv4 addresses. Anything no pattern matches is sent as written, including a contact address passed with `--email`. With `--session`, always run `--dry-run` first and let the user read the payload, because a transcript carries content they never reviewed. `npx --yes langwatch report --help` explains the options.

LangWatchQL analytics is switched per project. If any chart command answers with error code `lwql_not_enabled`, the feature is off for this project — tell the user, do not retry.

## Step 1: Discover the schema before writing any SQL

Never guess dataset or column names. The schema command lists every dataset your credentials may query, each column's type and description, and a runnable example query per dataset:

```bash
langwatch chart schema -f json
```

Read the datasets, their grain, their time column, and which columns are `available` to you. Saving validates the SQL against the analytics policy and the specification against the chart policy — it does not check that every column exists, so SQL naming a wrong column saves fine and only fails when the chart runs. Writing SQL against the schema you just read, then test-running the chart right after saving it (Step 3), is what catches a bad column before anyone sees it on a dashboard.

## Step 2: Write the SQL, using the reserved period parameters

A chart that should follow the dashboard's period selector declares the reserved bound parameters instead of hardcoding dates:

- `{dashboard_context_period_start:DateTime}` / `{dashboard_context_period_end:DateTime}` — the surface's period, half-open `[start, end)`
- `{dashboard_context_granularity_seconds:UInt32}` — the surface's datapoint step, in seconds

```sql
SELECT
  toStartOfInterval(OccurredAt, INTERVAL {dashboard_context_granularity_seconds:UInt32} SECOND) AS bucket,
  count() AS traces
FROM analytics.traces
WHERE OccurredAt >= {dashboard_context_period_start:DateTime} AND OccurredAt < {dashboard_context_period_end:DateTime}
GROUP BY bucket
ORDER BY bucket
```

Your own parameters (`{since:DateTime}`, `{model:String}`, …) get their values from `--param`; never pass a value for the reserved `dashboard_context_*` names.

## Step 3: Save the chart, then prove it runs

```bash
langwatch chart create \
  --name "Traces per day" \
  --sql-file query.sql \
  --spec-file spec.json \
  -f json
```

`spec.json` is a Vega-Lite specification reading from `{"data": {"name": "query_result"}}`, with fields named exactly after the SQL's output columns. The save validates the SQL against the analytics policy and the specification against the chart policy before writing anything — a refusal means fix the input, not retry. It does not check column names against the schema, which is why the very next command is always a run: a chart that saves but names a wrong column fails only at run time.

See the numbers before placing it:

```bash
langwatch chart run <chart-id> \
  --start 2026-08-01T00:00:00Z --end 2026-08-08T00:00:00Z \
  --granularity 3600 -f json
```

`--start`/`--end` fill the reserved period parameters and `--granularity` the datapoint step, which only accepts the offered steps: `1` (second), `60` (minute), `3600` (hour). A chart whose SQL declares the reserved period parameters **requires** `--start` and `--end` on every run — there is no default window, and running without them is refused. Only a chart that declares none of them runs without the flags.

## Step 4: Place it on a dashboard

```bash
langwatch dashboard list -f json          # find or create the target
langwatch chart place <chart-id> --dashboard-id <dashboard-id> -f json
```

With no `--grid-row`, the platform allocates the next free row, so it never lands on top of an existing chart. `langwatch chart unplace <chart-id>` takes it off again without deleting it.

## Managing saved charts

```bash
langwatch chart list -f json
langwatch chart get <chart-id> -f json    # SQL, parameters, spec, placement
langwatch chart update <chart-id> --sql-file query.sql
langwatch chart delete <chart-id>
```

## Failure modes worth knowing

- `lwql_not_enabled` — the project's LangWatchQL switch is off; stop and say so.
- `saved_workbench_charts_disabled_for_playground` — the custom-chart-playground is enabled for this project, which turns `chart` commands off; do not retry any `chart` command. Use the `dashboard-widgets` skill / `langwatch dashboard-widget` commands instead.
- A save that succeeds but a run that fails naming a column — the SQL names one that does not exist; re-read the schema (Step 1) and fix the column, then update the chart.
- `saved_workbench_chart_specification_refused` — the Vega-Lite specification breaks the chart policy; simplify it (one `query_result` data source, fields matching the SQL columns).
- `saved_workbench_chart_dashboard_not_found` — the dashboard id is not in this project; list dashboards again.

