Clay tables — inspect, query, and export data from an existing table via the `clay tables` CLI (list, columns, rows, query-live, structured query). To investigate a table or record, use the focused `/tables-*` skills (analyze, trace, error-sweep, value-trace, capacity).
Scanned 9/3/2026
Install to Claude Code
npx -y skills add clay-run/agent-plugins --skill tables-cli --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Tables Cli?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/clay-run-tables-cli)More formats (shields.io, HTML) on the badges page.
---
name: tables-cli
description: Clay tables — inspect, query, and export data from an existing table via the `clay tables` CLI (list, columns, rows, query-live, structured query). To investigate a table or record, use the focused `/tables-*` skills (analyze, trace, error-sweep, value-trace, capacity).
---
Clay tables are spreadsheet-like: columns (basic, formula, or enrichment), rows
(usually people or companies), and sources that add rows. This skill only **reads**
tables that already exist. If `clay` isn't on PATH or `clay whoami` fails on auth,
run the `setup` skill.
## Finding a table
Resolve it to a `tbl_...` id. Use `clay tables list --filter workbook.id=<wb_...>`
when the workbook is known (`clay workbooks list`), otherwise `clay tables list` and
pick by `.name` with `jq`. Do not use `--filter queryEnabled=true` unless you only
want query-synced tables — it hides the rest. If the user named no table or workbook,
ask rather than sweeping the workspace.
ID prefixes: `tbl_` table, `f_` column, `rec_` row, `wb_` workbook.
## Not supported
- **Creating tables** (or adding fields/columns). Tell the user to create the table
in the Clay app first; you can work with it once it exists.
- **Writing data.** The CLI cannot insert rows, update cells, or trigger enrichments.
`clay tables update` only toggles whether a table is queryable (`--query-enabled`);
it does not write data. Rows enter through the Clay app. The Public API is
query/list-only as well.
## CLI: `clay tables`
Prefer `columns` / `get` / `rows` / `query-live` for everyday work; use
`clay tables query` when you need synced Enterprise joins/pagination.
**Availability:** `clay tables query` requires **API table sync** (Enterprise).
Without sync, `clay tables query` and `clay tables update --query-enabled true`
fail with `auth_forbidden` ("API table sync is not enabled for this workspace") —
that's an account limitation, not a bug: don't retry or re-login. Fall back to
`columns` / `rows` / `query-live` (no sync required), and tell the user to contact
Clay about Enterprise access for synced `query`.
`clay tables --help` (and `clay tables <cmd> --help`) is the authoritative spec —
read it for exact flags, JSON shapes, and error codes.
### Reading rows vs. querying
Pick by how much querying power you need:
- **Read rows directly** — `clay tables get`, `columns`, `rows list/get`. Works on
any table with no setup. Filtering is exact-match only (`rows list --filter col=value`,
ANDed together). Best for quick lookups and pulling cell values as-is.
- **Query live (ClayQL)** — `clay tables query-live`. Structured ClayQL against the
table's live Postgres data — no Enterprise sync required. Use `columns get` / `get`
to learn the schema before writing the query.
- **Query (synced)** — `clay tables query`. Richer: range / contains / OR filters,
**joins across tables**, sorting, grouping, and paging past 100 rows. The table must
first be **enabled for querying** (below); requires API table sync (Enterprise).
Reach for `query-live` or `query` the moment a plain `rows list --filter` can't
express what you need — a range or text match, an OR, a join, sorting, grouping, or
a large ordered pull. Prefer `query-live` when sync isn't enabled; use synced `query`
for multi-table joins and cursor paging (and for a complete large pull when sync is
available — `query-live` is capped at 100 rows per call; page with `LIMIT n OFFSET m`
in `--query`). Otherwise a direct `rows list` is faster and needs no setup.
### Row ordering
Both `rows list` and `tables query` return rows in a stable, consistent order that
roughly tends to follow the order rows were created — but treat that as a loose
tendency only, never a guarantee. It's approximate, the two commands don't necessarily
order rows the same way as each other, and neither matches the order rows appear in
the Clay app. So don't rely on position for anything: don't map a row's position in
the output to what the user sees on screen, and don't read "the first / most recent
N rows" from position. To find a **specific record**, filter by an identifier
(`rows list --filter`, or a `tables query` / `query-live` filter) rather than relying
on position. Caller-controlled order is available on both query paths: `tables query`
via `order_by` (single page only — can't combine with cursor paging), and `query-live`
via ClayQL `ORDER BY` (also subject to the 100-row cap / `LIMIT`/`OFFSET` paging).
### List tables
Discover tables and their ids. Each row carries a `queryEnabled` flag for whether the
table is enabled for querying.
```bash
clay tables list # all tables
clay tables list --filter queryEnabled=true # query-sync-enabled only
clay tables list --filter workbook.id=wb_123 # tables in one workbook
clay tables list --filter owner.id=1417322 --limit 50 # by owner
clay tables list --limit 50 --cursor cursor_abc123 # pagination
```
### Run a live ClayQL query
`clay tables query-live` runs ClayQL against the table's live Postgres data — no
Enterprise sync required. Learn column names with `columns get` / `get` first; omit
any `FROM` clause (the table comes from `<tableId>`):
```bash
clay tables columns get tbl_abc123
clay tables query-live tbl_abc123 --query 'SELECT {{Name}}, {{ARR}} ORDER BY {{ARR}} DESC LIMIT 10'
```
See `clay tables query-live --help` for output shape and errors. Output is
`{ query, queryName?, rowCount, results }` — rows live under **`results`**, not `data`.
Each call returns at most **100 rows**; page with `LIMIT n OFFSET m` in `--query` (bump
`OFFSET` until a page returns fewer than `n` rows). Prefer this over synced `query` when
API table sync isn't enabled, or for a quick single-table look. For a complete large
export when sync _is_ available, prefer synced `query` + cursor paging instead.
### Enable a table for querying
`clay tables query` can only read a table that's been **enabled for querying**. Toggle
it with `update`:
```bash
clay tables update tbl_abc123 --query-enabled true # { id, queryEnabled: true }
clay tables update tbl_abc123 --query-enabled false
```
- **Not instant.** Enabling prepares the table in the background. A `query` run too soon
may return no/partial rows; retry after a short wait. Larger tables take longer.
- **Limited per workspace.** Check `clay tables query-usage` (`{ used, limit }`); at the
cap, disable a table before enabling another. A `limit` of 0 means API table sync isn't
enabled for the workspace (see the availability note above).
### Run a structured query
`clay tables query` takes a **structured JSON query** that supports **joins across
multiple tables** and cursor pagination — so it's the right choice for complex queries
or reading past 100 rows. The query is read from a file or stdin via `--query`.
```bash
clay tables query --query ./query.json | jq '.data | length'
echo '{"tables":[{"id":"tbl_abc123"}]}' | clay tables query --query - --limit 100
clay tables query --query ./query.json --limit 100 --cursor cursor_abc123
```
- The `--query` payload is the query itself (what to fetch); pagination is separate.
Minimal shape: `{ "tables": [{ "id": "tbl_..." }] }`. Beyond `tables`, it may include
`filter`, `select`, `join`, `order_by`, `group_by`, and `field_mode`. Field references
can use ids or names. See `clay tables query --help` for the most up to date information.
- Pagination is via flags: `--limit <n>` (1–100, default 50) and `--cursor <token>`.
When more rows remain, the response includes a top-level `cursor` — pass it back via
`--cursor` to fetch the next page.
- Output is `{ data: [ { "<fieldId>": <cell> } ], cursor?, fields? }`, where each `<cell>`
carries a `status` (`success` / `error` / `running` / `queued` / `retry` /
`rate_limited` / `awaiting_callback` / `empty`) plus its value.
Typical flow: `clay tables list --filter queryEnabled=true` to find the id → (if needed)
`clay tables update <id> --query-enabled true` → wait, then `clay tables query`. To
export, convert the JSON `data` array to CSV with the Write tool.
### Saving query results to CSV
After a `query-live` or `query` run, save the results locally as a CSV so the user can
access them. Convert the row array to CSV and write it with the Write tool — use
**`.results`** for `query-live` and **`.data`** for synced `query`. For `query-live`,
page with `LIMIT`/`OFFSET` and append each page so the CSV isn't truncated at 100 rows.
1. Run `clay tables query-live` or `clay tables query`
2. Take rows from `.results` (`query-live`) or `.data` (`query`); for live queries, keep
paging until a page is short
3. Extract column headers from the first result row
4. Convert each row to comma-separated values (quote fields containing commas)
5. Write to a local file like `./query-results.csv`
## Example: join across tables
"Join our Accounts and Contacts tables and pull the contacts at software companies":
**1. List tables to get their ids.**
```bash
clay tables list --filter queryEnabled=true | jq -r '.data[] | [.id, .name] | @tsv'
# tbl_accounts123 Accounts
# tbl_contacts456 Contacts
```
**2. Get each table's schema** so you know field ids, types, and the join key.
```bash
clay tables columns get tbl_accounts123
clay tables columns get tbl_contacts456
```
Say `Accounts` has `f_industry` (text) and `f_account_id`, and `Contacts` has
`f_company` that references the account.
**3. Enable querying if needed** (see above — not instant), then run the structured
query. Use the field ids from step 2.
```bash
clay tables update tbl_accounts123 --query-enabled true
clay tables update tbl_contacts456 --query-enabled true
```
```bash
echo '{"tables": [{ "id": "tbl_contacts456" }, { "id": "tbl_accounts123" }], "join": [{ "table": "tbl_accounts123", "on": { "left": "f_company", "right": "f_account_id" } }], "filter": { "field": "f_industry", "op": "contains", "value": "software" }}' | clay tables query --query - --limit 100 | jq '.data | length'
```
**4. Page past 100 rows** by passing the previous response's `cursor` back via
`--cursor` until the response no longer returns one:
```bash
echo '{"tables": [{ "id": "tbl_contacts456" }, { "id": "tbl_accounts123" }], "join": [{ "table": "tbl_accounts123", "on": { "left": "f_company", "right": "f_account_id" } }], "filter": { "field": "f_industry", "op": "contains", "value": "software" }}' | clay tables query --query - --limit 100 --cursor CURSOR_FROM_PREVIOUS_RESPONSE | jq -c '.data[]'
```
`clay tables query --help` lists the top-level query keys and pagination flags. Inner
shapes (filter ops, join `on`, `select` / `group_by`) are in the developer docs:
https://developers.clay.com/llms.txt
## Investigating tables
Beyond querying data, the CLI's read commands (`clay tables get`, `columns list|get`,
`rows list|get`) support diagnostic work. These need no query sync. Each investigation
is a focused skill:
| The user wants… | Skill |
| ------------------------------------------------------------------ | --------------------- |
| "what does this table do?" / "explain the {table} workflow" | `/tables-analyze` |
| "trace {id}" / "where is {id} and what state is it in?" | `/tables-trace` |
| "what's erroring in {table}?" / "show failed rows" (no identifier) | `/tables-error-sweep` |
| "why is {id} stuck/failing?" / "where did {value} come from?" | `/tables-value-trace` |
| "why aren't new rows being added / why is the import stuck?" | `/tables-capacity` |
`/tables-analyze` and `/tables-value-trace` share the column-DAG extraction recipe in
`tables-cli/dependency-catalog.md`.
## Rebuilding a Table as a Workflow
If a user wants to convert their table logic into a Clay workflow (reusable, branching,
or scheduled), use the `workflows` skill to rebuild the enrichment pipeline as
connected nodes.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!