Documentation · the CLI

Drive it from
your own
Claude
Code.

TableBI is a CLI your agent drives. Pipe every source into one trusted set of numbers and the lossless raw underneath; Ask in two altitudes; Pin a live dashboard that refreshes itself. Every command speaks --json. No LLM inside — your agent is the brain.

$0 inference Numbers are deterministic & traceable Pipe · Ask · Pin
~/clients/acme · zsh ● cli
$ tablebi install
✓ skill planted → ~/.claude/skills
$ tablebi context --json
definitions · sources · freshness · overview
$ tablebi query --kind compare --view metrics \
--metrics cost,roas --by platform --json
▸ blended, cross-channel, trusted
01 · Quickstart

From zero to a live dashboard

Five moves: plant the skill, connect a source, rehydrate, ask in both altitudes, pin it live. Run them yourself, or just tell your agent what you want — once the skill is installed it knows the commands.

01 Install

Plant the skill

Drops a skill into Claude Code (~/.claude/skills) or Codex (~/.agents/skills) so the agent knows TableBI exists and when to reach for it. Binary on PATH ≠ agent knows it.

terminal
# teach your agent the CLI exists + how to drive it
tablebi install

# also plant it for Codex
tablebi install --codex
02 Pipe

Connect a source

connect opens the platform's OAuth screen, lists what you can pick, then runs the first sync and builds an overview dashboard. A CSV is one upload with a label you choose.

terminal
# live: authorize in the browser → pick the property → first sync
tablebi connect ga4 -w acme
# several properties? it lists them; rerun with the one you want
tablebi connect ga4 --account 123456789 -w acme

# a channel with no live connector: upload its export under a label
tablebi connect csv --file tiktok-ads.csv --platform tiktok_ads -w acme
03 Rehydrate

Start every session here

context returns the metric definitions, connected sources, freshness, a data overview, and the command cheatsheet — in one call. pending lists sources that are stale or not connected yet.

terminal
# the live manifest: definitions + sources + freshness + dashboards
tablebi context -w acme --json

# what needs attention: stale or unconnected sources
tablebi pending -w acme --json
04 Ask

Both altitudes

query --view metrics for the cross-channel unified view; query --view a platform's own pack, or ask with SQL over its raw table, for its native fields. Same data underneath.

terminal
# unified — cross-channel, last 28 days vs the 28 before
tablebi query --kind compare --view metrics --metrics cost,roas \
  --by platform -w acme --json

# raw — native, single-platform, full granularity
tablebi ask -w acme --json \
  "SELECT campaign, SUM(clicks) AS clicks, SUM(impressions) AS impressions
   FROM meta_ads_raw GROUP BY campaign ORDER BY clicks DESC LIMIT 10"
05 Pin

Publish it live

dashboard publish hands back a hosted read-only URL. It stores the query (the spec), not a snapshot — so it refreshes itself every time the data does.

terminal
# start from a template for the sources you connected, edit, then publish
tablebi dashboard template seo-overview -w acme > board.json
tablebi dashboard publish --file board.json --dry-run -w acme
tablebi dashboard publish --file board.json -w acme

→ https://acme.tablebi.com/d/dsh_7f2a…  # read-only, live
⌘

One workspace = one client/brand (default default). Most commands take -w <ws>. Every read command accepts --json — so your agent parses, never guesses.

02 · The contract

What TableBI does, and what your agent does

TableBI gives you trustworthy numbers — it does the data cleaning, the unified metric definitions, and the freshness accounting — plus the lossless raw underneath. The reasoning runs on your own Claude Code. The line is clean, and it never blurs.

TableBI provides
  • — Deterministic numbers: cleaning, unified definitions, WoW math.
  • — Lossless raw: every native record kept, queryable.
  • — Trust metadata: freshness + the definition lineage on every number.
  • — Managed connectors, sync, and a live dashboard URL.
Your Claude Code provides
  • — The analysis: why it changed, what to do next.
  • — Reading the JSON and reasoning over it.
  • — Picking the altitude per question (unified vs raw).
  • — The brain. $0 inference — your key, your agent.

Numbers come from the CLI, never from the model — so they're reproducible and traceable. Every --json payload carries a trust block: freshness per source, and the lineage (each metric's formula) to cite when you explain a result.

03 · Pipe · connect

Connect once, kept synced

Live connectors are authorized once through the platform's own OAuth screen. After that, a source re-syncs when someone opens one of its dashboards and it has passed its cooldown (Google Ads 3 hours, Meta 6, GA4 12, Search Console 24), and a daily pass keeps the sources behind published dashboards fresh even when nobody opens them. Each refresh re-reads a trailing window, because every platform revises its recent days. The target you pick on the first connect persists. A CSV is one upload; upload again to add rows.

connect & sync
# provider = gsc | ga4 | google_ads | meta_ads; several targets → it lists them
tablebi connect google_ads -w acme
tablebi connect google_ads --account 1234567890 -w acme   # Google Ads
tablebi connect gsc --site "sc-domain:acme.com" -w acme    # Search Console

# pull again now; --days widens the window (pages and queries default to 90 days)
tablebi sync gsc --site "sc-domain:acme.com" --days 180 -w acme

# CSV: the label is yours — never the name of a platform that already syncs live
tablebi connect csv --file spend.csv --platform tiktok_ads -w acme

Data lags are real and TableBI accounts for them. Search Console is pulled as Google's fresh data, a few hours behind; the last two days are preliminary and Google revises them upward, so every sync re-pulls that range. GA4 keeps processing the most recent 24–48 hours. Meta keeps revising conversions after the click. Don't report "today" or "yesterday" as final — check freshness in context first.

04 · How your agent learns the data

It interrogates the data before it queries

An agent only knows what's in its context — and capabilities and data detail live in the CLI, not in this page, so they never drift. The loop, cheapest first: rehydrate with context, look at real rows with context --sample, then ask with query and fall back to ask. Output is always bounded — safe to read into context.

tablebi context

Rehydrate

Metric definitions, connected sources, freshness, data overview, command cheatsheet — one call.

context → grammar

See the fields

For each connected metric pack: its dimensions, metrics and the tables behind it, plus the SQL views you can query directly.

tablebi values

See the values

The top values a dimension takes (--dimension campaign), so filters hit real data.

tablebi context --sample

See the shape

A few real rows at full grain, so the agent reads the actual value shapes before it writes a query.

# what can I ask Search Console for here?
$ tablebi context -w acme --json
→grammar.packs: search_console · dims date, site, query, page, country, device · no campaign
# what campaigns can I filter on?
$ tablebi values --dimension campaign -w acme --json

This is the discovery layer under Pipe → Ask → Pin. Because the CLI is its own source of truth — context and --help are generated from the code and your live warehouse — the agent reads current capabilities, not a doc that rots.

05 · Ask · two altitudes

Trusted numbers. Full granularity. Both.

Two altitudes over the same files. Your agent moves between them in one breath: unified metrics for the cross-channel story, raw when it needs a platform's own metric. The rows each connector pulls are kept as the platform reported them — you never trade trust for power.

Unified · cross-channel · trusted

One set of numbers
you can trust.

One definition of cost, conversions, ROAS — reconciled across every platform. The number you quote the client.

$ tablebi query --kind breakdown --view metrics \
--metrics roas --by campaign --window last-7d \
-w acme --json
$ tablebi query --kind kpi --view metrics --metrics roas --window 2026-01-01:2026-01-31 -w acme --json
Blended ROAS · by campaignall sources
Prospecting4.6×
Retargeting3.9×
Brand Search0.9×
raw · native · single-platform · lossless

The platform's own
fields, intact.

The native columns each connector pulls — Meta's reach and frequency, GA4's channels and AI-assistant sources, Search Console's queries and pages. Read-only SQL, one platform at a time.

$ tablebi context --sample -w acme --json
→ meta_ads_raw: campaign, reach, frequency, cpm…
$ tablebi ask -w acme --json \
"SELECT campaign, SUM(reach) AS reach
FROM meta_ads_raw GROUP BY campaign"
Meta Ads · by campaign · nativesingle source
CampaignReach
Prospecting · Q3154,900
Retargeting · Q3126,500
Altitude Answers Command
Unified Overall? Blended ROAS? Cross-channel compare? query --view metrics · dashboard
raw · native This platform's own metric, at full granularity? query --view <pack> · ask "<SQL>"

Cross-platform blending always goes through the unified metrics view, never raw. Dimensions: date · platform · account · campaign (the live connectors sync at campaign level; Search Console's query / page / country / device stay in its own tables). Windows: last-7d · last-28d (default) · last-90d · last-<N>d · this-month · last-month · all · YYYY-MM-DD:YYYY-MM-DD, anchored on the last day that has data.

06 · Pin · publish

Pin a dashboard that refreshes itself

A pinned dashboard is a spec — declarative widgets — not a snapshot. Publishing hands back a hosted, read-only URL; the engine re-runs the query every time the data moves, so the link is always live. You build and edit it from Claude Code by editing the spec.

publish & edit the spec
# create or update from a spec file, after a dry run that compiles and runs every widget
tablebi dashboard publish --file board.json --dry-run -w acme
tablebi dashboard publish dsh_7f2a… --file board.json -w acme

# inspect the current dashboard payload
tablebi dashboard show dsh_7f2a… -w acme --json

# build/edit: read the spec → edit JSON → save it back
tablebi dashboard spec dsh_7f2a… -w acme --json > /tmp/spec.json
tablebi dashboard set-spec dsh_7f2a… --file /tmp/spec.json --confirm -w acme

A spec is { version: 2, title, defaults: { view, window, filter, lang }, widgets: [{ kind, view, metrics, by, window, compare, filter, limit }] }, where kind is kpi · trend · breakdown · table · compare, plus sql as a marked escape hatch. Up to 12 widgets per dashboard. dashboard template starts you from the sources you connected; unpublish takes a link down. Without --confirm, set-spec only previews the change. The web UI renders whatever spec is saved; the entry point stays the agent, not a GUI.

07 · Connectors

Sources, kept connected

Connectors handle auth, incremental fetch and an encrypted credential vault, so each source stays live and refreshed — not imported once and forgotten. CSV is one-shot; the rest connect once via OAuth, then sync.

Source Provider Connect via First-sync target
Google Search ConsolegscOAuth--site
Google Analytics 4ga4OAuth--account
Google Adsgoogle_adsOAuth--account
Meta Ads betameta_adsOAuth--account
CSV uploadcsvFile upload--file --platform
TikTok Ads · Google Sheets—on the roadmapnot shipped

A CSV upload is tagged with a free-text --platform label and normalized into the same definitions — that is how channels without a live connector get in today. New sources implement one interface — auth plus incremental fetch — and inherit the unified normalization for free.

08 · Bring your own agent

Claude Code or Codex. Your brain, your key.

TableBI ships the data and the deterministic engine; the reasoning runs on your own agent. The CLI is the primary surface — CLI-native agents are great at running commands and need no setup. tablebi install plants the skill so the agent learns the commands, the metric definitions, and the workflows.

Claude Code

Skill lands in ~/.claude/skills. Your agent reads context first, then drives the CLI in plain English — no prompt-engineering.

Codex

Skill lands in ~/.agents/skills (tablebi install --codex). Same CLI, same definitions, same --json contract.

↳

We host no LLM. Inference is $0 and numbers are deterministic & traceable — they come from the CLI, not a model. Hosts that prefer MCP get the same CLI as a local stdio server: tablebi install --mcp registers tablebi mcp (context, query, sql, define, dashboards) — same endpoints, same login. There is no hosted MCP endpoint.

Commands in the current CLI · every one takes -w <workspace> and --json
tablebi loginbrowser (device-code) sign-in
tablebi logoutsign out
tablebi context [--full]one-shot rehydrate: definitions, sources, freshness, dashboards, command signatures
tablebi query --kind <kpi|trend|breakdown|table|compare> --view <pack> --metrics <m,…>ask for a metric by name; the engine picks the table, window and joins
tablebi define [<key> <value>]workspace definitions: brand terms, site groups, conversion events, opportunity thresholds
tablebi mcpbuilt-in stdio MCP server (context, query, sql, define, dashboards); install --mcp registers it
tablebi disconnect <provider> --site|--account|--platform <target> --yesstop syncing a source and delete its data (asks for confirmation without --yes)
tablebi workspace delete <name> --confirm <name>delete a workspace with all its data, dashboards and authorizations
tablebi ask "<SQL>"read-only SQL over metrics, facts and the per-platform raw tables (alias: sql)
tablebi scoreboard [--window <days>]which Search Console sites are growing or slipping
tablebi gsc query | inspect | sitemaps --site <site>live Search Console calls for what the synced tables cannot answer
tablebi connect <gsc|ga4|google_ads|meta_ads> · connect csv --file <f> --platform <label>authorize and sync a source
tablebi sync <provider> --site <url> | --account <id> [--days <n>]pull a connected source again
tablebi pin --file <spec.json>save, validate and publish a live dashboard in one step
tablebi dashboard <list|show|spec|set-spec|publish|unpublish|annotate|template>manage dashboards; publish --file <board.json> [--dry-run] ships a grammar dashboard
tablebi install [--codex] [--mcp]plant the skill for Claude Code / Codex; --mcp also registers the MCP server
tablebi updateupgrade the CLI in place (volta or npm)
09 · Architecture

A deterministic engine. No LLM inside.

"What happened" is plain, explainable computation — connectors pull raw data, normalization unifies the definitions, metrics derive once, and the same compiler serves query, ask and every dashboard. That's why a number is the same no matter how you ask for it.

01

Facts

Per-tenant Parquet on object storage (Tigris/S3) + a DuckDB pod for the full history — both altitudes (unified + raw) live here.

02

Metadata

Postgres holds workspaces, sources, dashboard specs and encrypted credentials — small, transactional, nothing heavy.

03

Engine

One shared service layer derives every metric once — deterministic, traceable, $0 inference. The CLI and web are thin shells over it.

Give your Claude Code
a data backend.

Install the skill, connect your sources, ask in two altitudes, pin a live dashboard. Your key, your agent, your numbers.

$0 inference Your agent is the brain Pipe · Ask · Pin