well_query_records
Query records from Well's database.
⚠️ WORKFLOW:
1. To SHOW the user a table of a record type, just omit `fields`. You never choose
columns for presentation: the table the user sees is ALWAYS the root's display
view in the Well web app's column order, trimmed on the widest roots to what fits
a chat-width table.
2. To answer a targeted question, call well_get_schema(root) FIRST to discover
available fields, then name in `fields` ONLY the extra values you need (5-15
typically). They are ADDED to the display view in the payload you read — they do
not replace, reorder, or trim the columns the user sees.
ROOTS (read-only — all 33): companies, people, connectors, invoices, documents, transactions, accounts, payment_means, workspace_connectors, memberships, cards, checks, ledger_accounts, journals, journal_entries, tax_rates, exchange_rates, invoice_transactions, categories, account_balances, tasks, workspaces, invoice_payment_means, chat_conversations, blueprint_runs, workspace_connector_sync_logs, media, emails, phones, web_links, locations, invoice_items, billing_events
(The accounting graph — ledger_accounts, journals, journal_entries — and balances/rates are read-only projections owned by the sync/posting pipelines; query them for financial context, you cannot create/update them here. Sub-resources like emails/phones/locations are usually richer when read via their parent company/person.)
CONNECTED TOOLS: do NOT use this tool to show the user what they have connected — call well_list_connectors instead. It owns that job: connection status, and an install link for anything not connected yet. Query root "workspace_connectors" here only for genuine RECORD-level needs — reading sync timestamps, filtering connections, joining them with other roots. ("connectors" is the installable catalog; "workspace_connector_sync_logs" is per-sync history.)
Well already syncs the providers' data into the roots above — invoices, transactions, accounts, the accounting graph. ALWAYS read it from here. well_invoke_connector_tool and a provider's own tools are for an ACTION the user explicitly asked to take on that provider (e.g. "create this record in Attio"), never a way to fetch data Well already holds.
EXAMPLE - show the user their invoices (no `fields`, ever):
well_query_records({ root: "invoices", limit: 50 })
EXAMPLE - answer "how much is still owed on the unpaid invoices?":
well_query_records({
root: "invoices",
fields: [["invoices", "balance_due"]],
whereClause: { "payment_status": { "_in": ["unpaid", "partial"] } }
})
// balance_due arrives in the rows for you to total up; the user still sees the
// standard invoices table, with its identity, counterparty and status columns.
⚠️ RULES:
- `fields` is ADDITIVE — it widens the data you receive, never the table the user sees
- Omitting fields (default view) or naming a few extras both beat allFields
- Field paths from schema: "invoices.issuer.name" → ["invoices", "issuer", "name"]
- Default 50 records per request, max 500. Use cursor pagination for more.
PAGINATION (cursor-based):
- First call: omit cursor. Response includes nextCursor.
- Next page: pass the returned nextCursor as cursor.
- Last page: nextCursor is null.
Example:
Page 1: well_query_records({ root: "invoices", fields: [...], limit: 50 })
→ { rows: [...], nextCursor: "eyJpZCI6MTAwfQ==" }
Page 2: well_query_records({ root: "invoices", fields: [...], limit: 50, cursor: "eyJpZCI6MTAwfQ==" })
→ { rows: [...], nextCursor: null } // last page
FILTERING (whereClause):
- Uses Hasura-style operators on field names.
- Safe operators (work on ALL field types): _eq, _neq, _in, _nin, _is_null
- Numeric/date only: _gt, _gte, _lt, _lte
- Text only: _like, _ilike
- When unsure of a field's type, prefer _eq or _in (they always work).
- Combine with _and, _or, _not
- For relationship fields, use nested syntax: { "issuer": { "name": { "_ilike": "%acme%" } } }
Examples:
{ "status": { "_eq": "unpaid" } }
{ "grand_total": { "_gt": 1000 } }
{ "local_currency": { "_eq": "EUR" } }
{ "_and": [{ "status": { "_eq": "unpaid" } }, { "grand_total": { "_gte": 500 } }] }
{ "issuer": { "name": { "_ilike": "%acme%" } } }
SORTING (orderBy):
- Sort by any field: { field: "grand_total", direction: "desc" }
- Default sort is by primary key ascending.
Returns { rows, totalCount, nextCursor, success }.