Quill Documentation Audit
Quill's docs cover the core API surface but are riddled with cross-page contradictions, broken code samples, and undocumented packages — an AI agent or new developer following the Quickstarts verbatim will hit syntax errors, undefined references, and a wrong env-var name within minutes.
1. Server Quickstart code samples are broken in multiple ways (critical)
Location: https://quill.co/docs/server/quickstart.md
Problem: A single page — the canonical first-run experience for the Server SDK — contains multiple defects that prevent its samples from working as copy-pasted:
- The env-var name is misspelled
QULL_PRIVATE_KEY(noI) in BOTH the Node.js and Python examples; only the Go example is spelled correctly asQUILL_PRIVATE_KEY. - The Node.js example is missing a comma between object keys:
tenants: [{...}]\n metadata,— a hard JS syntax error. - The Go signature is
client.Query(organizationID, body.Metadata)(positionalorganizationIDstring), while the Node and Ruby examples taketenants: [{ tenantField, tenantIds }]. The same conceptual call has at least two different signatures across the four shown samples, with no note explaining why. - The Ruby example sets
database_type: "clickhouse"while every other example and tab uses"postgresql". ClickHouse appears in zero other doc pages, and the docs never enumerate the canonical set of validdatabase_typevalues. - Install commands are wrong on three of five tabs:
go get https://github.com/quill-sql/quill-go(go gettakes a module path, not an HTTPS URL),bundle install quill-sql(bundle installtakes no gem-name argument), andcomposer require quill.co/quill-php(the company domain is used as the vendor name, which is non-standard for Packagist).
Consequence: A developer following the Server Quickstart hits a JS syntax error on the very first paste; even after fixing it, the SDK silently fails to authenticate because process.env.QULL_PRIVATE_KEY is undefined. Agents copying these snippets cannot fix what they cannot detect — and the SDK shape they generate for Go vs. Node will be inconsistent depending on which sample they sampled. The Ruby ClickHouse line will mislead developers into thinking ClickHouse is a supported database_type.
The fix: Search-and-replace QULL_PRIVATE_KEY → QUILL_PRIVATE_KEY site-wide. Add the missing comma in the Node example. Either unify the cross-language signatures or call out explicitly that the Go SDK uses a different multi-tenancy model (and link to a dedicated reference). Replace the Ruby "clickhouse" with "postgresql" to match the other tabs. Fix the install commands: go get github.com/quill-sql/quill-go, gem install quill-sql (or a Gemfile entry), and a Packagist-conventional composer require quill-sql/quill-php.
2. The tenants prop has four mutually inconsistent shapes across the docs (critical)
Location: /docs/react/quill-provider.md, /docs/react/chart.md, /docs/react/dashboard.md, /docs/react/quickstart.md, /docs/react/headless-dashboard.md, /docs/react/themes.md
Problem: QuillProvider's tenants prop is documented in the props table as type="array" with no schema. Across the docs, four different shapes appear, and one page never even uses the prop:
- chart.md:
tenants={{ tenantField: "customer_id", tenantIds: [2] }}— a bare object, not an array (twice on the page). - dashboard.md:
tenants={[2]}— an array of bare numbers. - quill-provider.md:
tenants={[user.organizationId]}— an array of bare ids. - quickstart.md / headless-dashboard.md:
tenants={[{ tenantField: "customer_id", tenantIds: [2] }]}— the array-of-objects shape. - themes.md: passes
organizationId={user.organizationId}toQuillProvider, a prop that does not exist in the props table at all.
The quill-provider.md props table marks tenants as not required but the prose says "Required if not passing a queryEndpoint." The "With OrgId" section header says "organizationId" while the code passes tenants.
Consequence: This is the single most-used prop in every example. A developer or AI agent cannot determine the correct shape without reading the source code; the chart.md shape (bare object) directly violates the documented array type and will likely throw a runtime error or silently ignore the value. Agents will inconsistently scaffold code depending on which page they happen to crawl first.
The fix: Pick one canonical shape (the array-of-objects form is the most expressive), update the ParamField to type="Array<{ tenantField: string; tenantIds: (string | number)[] }>", mark it required with the conditional spelled out, and rewrite every example on every page to match. Remove or document the organizationId prop in themes.md. Replace the "With OrgId" heading with "With Tenants" and align the prose.
3. The QuillReport schema disagrees with itself across two reference pages, and the cross-references are dead (critical)
Location: /docs/react/dashboard.md vs. /docs/react/use-dashboard.md
Problem: Both pages document the same QuillReport type but disagree on field types, required-ness, and field membership:
rows:{ [key: string]: any }[](not required) on dashboard.md vs.{ [key: string]: string }[](required) on use-dashboard.md.yAxisFields.format:stringon dashboard.md vs.AxisFormaton use-dashboard.md.pivot: not required on dashboard.md vs. required on use-dashboard.md.compareRows: not required on dashboard.md vs. required on use-dashboard.md.chartType: literal union'line' | 'pie' | 'table' | 'bar' | 'column' | 'metric'on dashboard.md vs. plainstringon use-dashboard.md.- use-dashboard.md adds 13 fields absent from dashboard.md (
template,pagination,sort,rowCount,queryString,filterMap,referenceLines,referenceLineYValues,includeCustomFields,columnsWithCustomFields,pivotRows,pivotColumns,pivotRowCount).
Dashboard.md compounds the issue with four Info callouts that all point to /components/dashboard#quill-report — a route that does not exist anywhere in the docs (the actual path is /react/dashboard#quill-report). All four are dead links.
Consequence: A TypeScript user typing against QuillReport will get different compile-time errors depending on which docs page they referenced. Agents writing code against report.pivotRows (used in headless-dashboard.md) will find the field is documented on one page but not the other, and may flag it as an error. The dead /components/... links break navigation for anyone trying to look up the schema.
The fix: Define QuillReport once in a single canonical reference location (e.g. /docs/react/types#quill-report), make every doc page link to that anchor instead of duplicating the schema, and fix every /components/dashboard#quill-report reference to point at the new canonical anchor.
4. format() has two contradictory call signatures and the headless example references an undefined component (critical)
Location: /docs/react/format.md vs. /docs/react/headless-dashboard.md
Problem: Two issues compound in the headless flow:
format.mddocumentsformat(value, formatType)as a positional-argument function:format(123.45, "dollar_cents").headless-dashboard.mdcalls it with a single object argument:format({ value: ..., format: ... }).- One of the two is wrong. The reader has no way to know which.
- Also on
format.md:valueis documented astype="string" requiredbut the example clearly passes numbers (123.45,123).
Separately, the headless-dashboard.md sample is itself broken: the CustomDashboard component renders <MetricReportsSection reports={sections["metrics"]} />, but the function defined immediately below is named MetricsSection, not MetricReportsSection. The referenced component does not exist in the file.
Consequence: The headless flow is the differentiated value-prop of Quill ("styled with your existing UI components"). The single end-to-end example for that flow does not compile (undefined reference) and uses a format() signature that contradicts the function's own reference page. Anyone — human or agent — copying this template will produce non-running code on the first try.
The fix: Pick one format() signature, update the reference page and every example, and add string | number to the value ParamField. Rename MetricsSection to MetricReportsSection (or vice versa) in the headless-dashboard.md sample so the file compiles, and run all docs samples through a TypeScript check in CI.
5. Production environment description is a copy-paste of Development, telling readers to use prod keys in dev apps (critical)
Location: /docs/bi-platform/overview.md
Problem: The canonical concepts page documents two environments. The Development bullets read:
- "Development Environment publicKey is used in your development and staging react apps"
- "Development Environment privateKey is used in your development and staging server apps"
The Production bullets read:
- "Production environment publicKey is used in your development and staging react apps"
- "Production environment privateKey is used in your development and staging server apps"
Both environments' keys are documented as belonging to development and staging. Production is never assigned to production apps anywhere on the page.
Consequence: This is a deployment-correctness bug, not a typo: it is the only place in the docs that explains environment-key mapping. A developer following this guide for key rotation or environment setup will configure their environments backwards — putting production keys in staging and either leaving production unkeyed or running production on dev keys. That is a security and reliability issue, with no other docs page to cross-check against.
The fix: Change the Production bullets to read "used in your production react/server apps." Also align the casing/terminology: the page uses privateKey (camelCase) while the server quickstart uses env var QUILL_PRIVATE_KEY — pick one convention and call out the env-var-vs-prop relationship explicitly.
6. Hard-coded real-looking publicKey shipped across every React example with no placeholder convention (significant)
Location: /docs/react/quickstart.md, /docs/react/dashboard.md, /docs/react/chart.md, /docs/react/headless-dashboard.md, and others
Problem: The same 24-character hex value publicKey="6579031b3e41c378aa8180ec" appears verbatim in code blocks across the React Quickstart, Dashboard, Chart, ReportBuilder, and headless examples. There is no placeholder convention (<YOUR_PUBLIC_KEY>, process.env.QUILL_PUBLIC_KEY, etc.) used consistently — some pages instead use process.env.QUILL_API_KEY, others use the literal hex, and the bi-platform/self-host.md example uses an unquoted-style placeholder publicKey="QUILL_PUBLIC_KEY".
Consequence: A 24-char hex value reads as a real, valid identifier. Developers and especially AI coding agents will copy it verbatim without realizing it must be replaced — they will then be confused when their dashboard either fails to authenticate or, worse, renders against a demo tenant they did not intend to query. The inconsistent placeholder style across pages compounds the problem because there is no recognizable signal that says "replace this."
The fix: Standardize on a single placeholder convention everywhere — publicKey={process.env.QUILL_PUBLIC_KEY} for env-var examples, and <YOUR_PUBLIC_KEY> for inline literals. Remove the hard-coded 6579031b3e41c378aa8180ec value site-wide. If the demo dashboard behind that key is intentional, call it out explicitly with an <Info> callout ("This example uses a public demo key; replace with your own from app.quill.co").
7. @quillsql/admin package is referenced but missing from the docs index and reference (significant)
Location: /docs/bi-platform/self-host.md (only mention) and /docs/llms.txt (absent)
Problem: The marketing site promises a "Management Toolkit" as one of three core product surfaces. The @quillsql/admin package is mentioned exclusively on self-host.md. It does not appear in /docs/llms.txt and has no API-reference page. The only export shown with any code is AdminProvider, and the only props ever shown for it are queryEndpoint and publicKey — there is no full prop table.
Compounding this, the only two AdminProvider code blocks both contain a typo'd, protocol-less URL: queryEndpoint="api.youserver.com/quill" (missing https:// and the host is misspelled youserver instead of yourserver). The page also has a grammar slip — "any component that might use to access BI Platform components."
The page is titled "Self-host the BI Platform" but the architecture page defines "Self-Hosted" as a deployment mode where the entire BI Platform runs in the customer's cloud. This page only describes embedding an AdminProvider into a React app — a different concept entirely.
Consequence: An AI agent indexing the docs via llms.txt will not know @quillsql/admin exists. A developer who finds it on the self-host page cannot answer basic questions: what is the auth model? What other exports does the package have? Copying the queryEndpoint value verbatim makes a relative-URL request to the host's own origin (and the host is misspelled even if a reader fixes the protocol). The title/concept mismatch with the architecture page leaves readers unsure whether they're configuring an embedded admin UI or a fully self-hosted BI deployment.
The fix: Add @quillsql/admin to llms.txt and create a dedicated reference page with a full prop table for AdminProvider (and any other exports the package ships). Fix both URLs to a syntactically valid placeholder such as https://api.yourserver.com/quill. Rename the page to "Embed the Admin Toolkit" (or split into two pages) and reserve "Self-host the BI Platform" for the architecture-style fully-self-hosted deployment described on architecture.md.
8. Mintlify <ParamField body> tag misused for React hook params on use-dashboard.md (significant)
Location: /docs/react/use-dashboard.md
Problem: The entire useDashboard reference page documents the hook's parameters and return values using <ParamField body="dashboardName" ...>, <ParamField body="isLoading" ...>, <ParamField body="sections" ...>, etc. The body attribute is Mintlify's tag for documenting POST/PUT request body fields — not React hook arguments. The correct tag for a function parameter is <ParamField path="...">, and for a return value it is <ResponseField>.
This is structural docs-tooling misuse, not stylistic preference: the entire reference page for one of the headless flow's two core hooks uses the wrong schema element. The same page also has terminology drift in its filter docs — preset, presetValue, and presetOptions all appear within a few hundred lines for what reads like the same concept (an enum/list-of-options for date filters), with no defining text linking them.
Consequence: Agents that parse Mintlify markdown structurally will categorize dashboardName as a request-body field instead of a function argument, mis-typing any tooling generated from the docs. The page renders visually (which is why the bug has survived) but every consumer that respects the underlying schema sees the wrong thing. The preset / presetValue / presetOptions drift means a developer wiring up a date-preset filter cannot tell which property name the hook actually reads.
The fix: Convert all parameter fields on use-dashboard.md from <ParamField body="..."> to <ParamField path="...">, and convert the ## Returns section to <ResponseField>. Audit the rest of the React hook docs (use-dashboard-report, use-quill, use-export, use-dashboards) for the same misuse. Pick one canonical name for the date-preset concept and use it consistently — define it in the page once and link from all three sites.
9. Architecture page contradicts itself on "Cloud" and "Self-hosted"; Metadata API undocumented (significant)
Location: /docs/architecture.md
Problem: The page opens with a comparison table listing the two TRADITIONAL BI architectures whose problems Quill solved: "Cloud" (iframe embedding) and "Self-hosted" ("still requires iframe embedding"). It then introduces Quill's THREE architectures — Hybrid, Self Hosted, and Cloud — using the same exact names for two of them. The reader cannot tell whether Quill's "Cloud" option is the bad-iframe-cloud just disclaimed, or a different thing.
Naming inconsistencies pile up: Self-hosted (table) vs. Self Hosted (H2 heading) vs. Self-host (slug and bi-platform/self-host.md title) — three spellings for one concept.
Both Hybrid and Self-Hosted depend on a "Quill Metadata API," but no docs page describes what that API is, what endpoints it has, what data is sent over it, or how to audit traffic to it. The marketing site emphasizes "Sensitive data never leaves your cloud," yet the docs cannot answer what does leave it.
Consequence: A buyer doing a security review cannot complete it from the docs alone — the metadata channel is undocumented. An engineer choosing between deployment modes cannot tell whether Quill's "Cloud" mode has the iframe drawbacks the same page disclaims. AI agents indexing this page will surface contradictory descriptions of the same word.
The fix: Rename Quill's three modes to disambiguate (e.g., "Embedded Hybrid," "Embedded Self-Hosted," "Quill-Hosted Cloud"). Standardize on one spelling (Self-Hosted) across heading, table, slug, and prose. Add a Metadata API reference page enumerating endpoints, payload schemas, and what is/isn't transmitted — this page is also the natural home for the egress IPs currently buried in bi-platform/quickstart.md.
10. themes.md ships invalid CSS via semicolon-separated font lists (minor)
Location: /docs/react/themes.md
Problem: The canonical custom-theme example sets fontFamily: "Inter; Helvetica" and chartLabelFontFamily: "Inter; Helvetica" — semicolons between font names. The CSS font-family separator is a comma. Copy-pasting either string into a stylesheet (or letting Quill forward it into a CSS rule) yields invalid CSS and a fallback to the browser default font.
Consequence: Developers themeing their dashboards with the documented example will see fonts silently fall back to defaults, with no error to indicate why. Because the theme object is the documented happy path for matching Quill's output to host-app branding, the brand-mismatch will be visible to every end user.
The fix: Replace both occurrences with comma-separated lists ("Inter, Helvetica") and add a brief note that the value is forwarded into a CSS font-family rule.
What they do well
- A
llms.txtfile exists at/docs/llms.txtlisting every page with title and URL — one of the few agent-friendly affordances in the audited surface. - The architecture page presents a clear three-mode mental model (once the naming is fixed) and is the right altitude for a buyer-facing concepts doc.
- Code samples consistently include a runnable
App.tsxwrapper rather than fragmentary snippets — when they compile.
Top 3 recommendations
- Fix the production-key mapping and the Server Quickstart immediately — the overview.md prod-keys-in-staging bug is a deployment-correctness landmine, and the typo'd
QULL_PRIVATE_KEYplus missing JS comma break the very first paste of the onboarding flow. Both are minutes-to-fix. - Define
tenantsandQuillReportonce, link everywhere; standardize on a<YOUR_PUBLIC_KEY>placeholder — every page that re-declares these types or hard-codes6579031b3e41c378aa8180ecdiverges. Centralize the types in/docs/react/types, replace duplicates with anchor links, and pick one placeholder convention for keys. - Add a Metadata API reference, an
@quillsql/adminreference, and fix the Mintlify schema misuse on the hook reference pages — all three are core to product credibility (security review, the marketing-promised "Management Toolkit," and parseable hook docs), and all currently fail their primary readers.