Discovery: explore, fields and tables

Five read routes describe a deployed branch without planning anything you have to run. They are what a field picker, an autocomplete or an agent calls before building a spec. Every one takes the same Authorization: Bearer header as /sql and works with query keys and personal keys.

routereturns
POST .../explorethe dimensions and measures a given query can still add
GET .../fields?q=&hidden=every field, optionally searched
GET .../tablesevery table with its fields
GET .../branches/{branch}the branch summary
GET /projectsthe projects and branches the key can see

... is /projects/{uid}/branches/{branch} throughout.

explore

Given a query, which other fields could join it without breaking it? Send the same body as /sql (spec or expr, optional q). A context is accepted and ignored: exploring is about the model, not about rows.

curl -s https://app.0sql.io/projects/tpcds/branches/main/explore \
  -H "Authorization: Bearer zqk_..." \
  -H "Content-Type: application/json" \
  -d '{"expr": "category, web net paid", "q": "date"}'
zsql explore --expr 'category, web net paid' -q date
date        dim  Date          web_sales, store_sales, date_dim  ~1.00
ws_sold_date dim Web Sold Date web_sales                         ~0.67
-- can add 2 dimensions, 0 measures · server 210 us
const res = await fetch("https://app.0sql.io/projects/tpcds/branches/main/explore", {
  method: "POST",
  headers: { Authorization: `Bearer ${key}`, "Content-Type": "application/json" },
  body: JSON.stringify({ spec: currentSpec, q: userTyped }),
});
const { dimensions, measures } = await res.json();
{
  "dimensions": [
    {"uid": "date", "name": "Date", "data_type": "date", "description": "Calendar date",
     "tables": ["web_sales", "store_sales", "date_dim"], "score": 1.0},
    {"uid": "ws_sold_date", "name": "Web Sold Date", "data_type": "date", "description": "",
     "tables": ["web_sales"], "score": 0.67}
  ],
  "measures": [],
  "total_us": 210
}
  • Without q every addable field is listed, dimensions and measures separately, and score is absent.
  • With q, only fields whose name contains the term (score 1.0) or that are trigram-near it (score at least 0.5) are kept, best first.
  • Hidden fields are never listed.

Use it to drive a picker: after each selection, send the current spec and show only what comes back, so the user cannot build a query the planner will refuse. Agents get the same guarantee: a loop of explore, pick, explore converges on a plannable spec without guessing field names.

fields

curl -s "https://app.0sql.io/projects/tpcds/branches/main/fields?q=paid" \
  -H "Authorization: Bearer zqk_..."
{"fields": [
  {"uid": "net_paid", "name": "Net Paid", "kind": "measure", "data_type": "decimal",
   "description": "Store sales net paid", "synonyms": ["revenue"], "tags": [],
   "hidden": false, "tables": ["store_sales"], "score": 1.0},
  {"uid": "ws_net_paid", "name": "Web Net Paid", "kind": "measure", "data_type": "decimal",
   "description": "Web sales net paid", "synonyms": [], "tags": [],
   "hidden": false, "tables": ["web_sales"], "score": 1.0},
  {"uid": "ws_paid_inc_tax", "name": "Web Paid Inc Tax", "kind": "measure", "data_type": "decimal",
   "description": "", "synonyms": [], "tags": [], "hidden": false, "tables": ["web_sales"], "score": 1.0}
]}
parameffect
qcontaining matches first (score 1), then fuzzy matches with their trigram score
hiddentrue includes hidden fields

Without q the whole catalogue is returned and score is absent. kind is dimension or measure. tags are the policy trigger tags from the model. CLI: zsql fields paid, zsql fields --hidden.

tables

curl -s https://app.0sql.io/projects/tpcds/branches/main/tables \
  -H "Authorization: Bearer zqk_..."
{"tables": [
  {"uid": "store_sales", "name": "Store Sales", "physical_name": "store_sales", "cost": 100,
   "datasource": "warehouse", "fields": ["net_paid", "ss_sold_date", "customer_id", "..."]},
  {"uid": "web_sales", "name": "Web Sales", "physical_name": "web_sales", "cost": 100,
   "datasource": "warehouse", "fields": ["ws_net_paid", "ws_sold_date", "..."]},
  {"uid": "item", "name": "Item", "physical_name": "item", "cost": 10,
   "datasource": "warehouse", "fields": ["category", "product_name", "..."]},
  {"uid": "date_dim", "name": "Date", "physical_name": "date_dim", "cost": 1,
   "datasource": "warehouse", "fields": ["date"]}
]}

cost is the model’s planning cost (the resolver prefers cheaper tables); fields lists field uids. The table uids are what the spec’s hints key accepts. CLI: zsql tables.

branch summary

curl -s https://app.0sql.io/projects/tpcds/branches/main \
  -H "Authorization: Bearer zqk_..."
{"project": "tpcds", "branch": "main", "deployed_at": "2026-10-01 15:04",
 "datasources": 1, "tables": 6, "fields": 42, "joins": 5, "paths": 9, "policies": 1,
 "tests": 3, "warnings": []}

The same counts a deploy prints, minus the test results. CLI: zsql status.

projects

curl -s https://app.0sql.io/projects -H "Authorization: Bearer zqk_..."
{"deployments": [
   {"project": "tpcds", "branch": "main", "deployed_at": "2026-10-01 15:04",
    "datasources": 1, "tables": 6, "fields": 42, "joins": 5, "paths": 9, "policies": 1, "tests": 3, "warnings": []}
 ],
 "projects": [
   {"uid": "tpcds", "name": "TPC-DS", "production_branch": "main", "visibility": "account",
    "protected_production": false, "level": "read"}
 ]}

deployments is one summary per deployed branch the key can read; projects lists each project with the caller’s access level (read, write or owner). A query key sees only the projects and branches it is granted. CLI: zsql list. See Accounts and keys.

Next steps