The docs are a Mintlify-built developer site for an agent platform with CLI, SDKs, devices, and a chat widget — but the canonical machine-readable surfaces (OpenAPI spec) and several authoritative pages contradict each other or point to placeholder content, which is exactly the failure mode that breaks AI coding agents trying to integrate.
1. Official OpenAPI spec is a Mintlify plant store sample served over plain HTTP (critical)
Problem: The OpenAPI spec advertised on llms.txt as the canonical machine-readable API contract is the unmodified Mintlify starter:
"title": "OpenAPI Plant Store",
"description": "A sample API that uses a plant store as an example...",
"servers": [ { "url": "http://sandbox.mintlify.com" } ],
"paths": { "/plants": ..., "/plants/{id}": ... }
There are no Lua endpoints. The server URL is http://sandbox.mintlify.com — plain HTTP, not HTTPS.
Consequence: This is the worst possible failure mode for agent ingestion. An AI coding agent (Cursor, Claude Code, Copilot) reading llms.txt will follow the "OpenAPI Specs" link, ingest a plant-store sample, and confidently generate code calling GET /plants against sandbox.mintlify.com — none of which exists in Lua. The http:// scheme also means autogenerated clients will emit insecure URLs by default. Humans notice; agents will silently produce wrong, insecure client code.
The fix: Replace openapi.json with a real spec generated from the Lua API (Agents, AI, Baskets, CDN, Data, etc., which are listed in llms.txt). Set servers[0].url to https://api.heylua.ai (note the scheme). Until that exists, remove the OpenAPI Specs block from llms.txt so agents don't cache the placeholder.
2. llms.txt promises a programmatic API surface that the spec doesn't deliver (critical)
Problem:llms.txt enumerates a rich API surface — Agents, AI, Baskets, CDN, Data, plus CLI commands — and points agents at api-reference/openapi.json for machine-readable access. But the only OpenAPI artifact is the plant store sample (Issue 1), so for every API in (e.g. , , , , ) there is no schema, no parameter typing, no auth scheme definition, and no error model that an agent can parse.
Consequence: The whole point of llms.txt is to give agents a manifest of structured docs. Lua's manifest is solid, but the structured payload behind it is prose-only Markdown. Agents must guess request shapes from English descriptions, which is exactly what llms.txt was designed to avoid.
The fix: Either generate per-resource OpenAPI fragments (api/agents.openapi.json, etc.) and link them from llms.txt, or publish a single combined spec covering every resource the docs document. Don't ship the manifest until the schemas exist.
3. Localhost guidance instructs developers to set environment: "production" (critical)
Problem: The widget refuses to load on un-whitelisted domains, and localhost cannot be whitelisted. The documented workaround is:
"Testing on localhost? localhost cannot be whitelisted. You must pass your config inline and set environment: "production" explicitly — this bypasses the domain whitelist check so the widget loads during local development"
Consequence: The docs are telling developers to label local dev traffic as production to bypass a security control. Anyone copying this snippet into a real app risks shipping it as-is — the literal string production is now in their initialization code, the whitelist is bypassed, and there's no instruction to remove it before deploy. It also means analytics, billing, or rate limits keyed on environment will be polluted by dev traffic.
The fix: Add a real development or localhost mode to LuaPop that bypasses whitelisting only when an explicit dev flag is set, then update the docs. If the workaround must remain, document the exact code path to remove before production deployment, and call out the security/billing implications in a Warning, not as a casual instruction.
4. No canonical API base URL reference; base URLs disagree across pages (significant)
Problem: There is no single "API base URL" page anywhere in the docs. Different surfaces imply different bases:
The OpenAPI spec on api-reference/openapi.json: http://sandbox.mintlify.com
The devices reference: https://api.heylua.ai (Socket.IO) and wss://mqtt.heylua.ai/mqtt (MQTT)
The marketing/admin surface: heylua.ai, admin.heylua.ai, docs.heylua.ai, blog.heylua.ai
llms.txt does not list a base-URL reference page either.
Consequence: A developer (or agent) trying to call any Lua REST API has no authoritative answer for "what hostname do I hit?" They will either trust the OpenAPI placeholder (sandbox.mintlify.com), guess api.heylua.ai from the devices doc, or invent something based on the marketing domain. Each path produces different broken behavior.
The fix: Publish a single "API base URLs" reference page enumerating every public endpoint (api.heylua.ai, mqtt.heylua.ai, admin.heylua.ai, etc.), what each is for, and which transports/protocols they speak. Link it from llms.txt and from every API resource page.
5. npm package names are unreconciled across docs (significant)
Problem:llms.txt's "Optional" block lists the npm package as lua-cli (https://www.npmjs.com/package/lua-cli). The chat widget install page tells developers to:
npm install @lua/pop
There is no single page that disambiguates which artifacts ship under which names: is @lua/* a real npm scope, what else lives in it, and how does it relate to the unscoped lua-cli package? The "Optional" links in llms.txt only mention lua-cli; @lua/pop is not listed there.
Consequence: A developer (or an agent) doing npm install lua or npm install @lua/cli based on inferred conventions will hit the wrong package or a 404. Agents extracting install commands from the chat-widget page won't know that @lua/pop is widget-only, and won't know whether other @lua/* packages exist.
The fix: Add a single "Packages" reference page enumerating every published artifact (npm scope, package name, what it's for, current version) and link it from llms.txt. Make sure both lua-cli and @lua/pop are listed, and clarify whether @lua/* is a published scope or only contains the widget.
6. Persona example contradicts itself within the same page (significant)
Process returns within our 45-day policy
...
AcmeCorp is an online retailer since 2010.
10,000+ products.
Free shipping over $50.
45-day return policy.
Business hours: 9 AM - 6 PM EST.
Return window changes (30 days → 45 days), shipping policy changes (2-day standard → free over $50), and the Good Persona introduces unrelated facts not present in the Context block ("online retailer since 2010", "10,000+ products", "9 AM – 6 PM EST hours").
Consequence: This is a docs page about how to write a persona that won't hallucinate. The page itself models inconsistency. A developer copying either block as a template internalizes contradictions, and an agent training on this page learns that the same persona may legitimately disagree with itself.
The fix: Use one consistent set of AcmeCorp facts across every example on the page. If the intent is to contrast "bad" and "good" personas, label which fields changed and why — don't quietly mutate unrelated business facts.
7. lsk_ API key prefix is undocumented (significant)
Problem: The "API Key" tab on the install page shows:
$ lua auth configure
? Choose authentication method: API Key
? Enter your API key: lsk_abc123def456...
The lsk_ prefix is never defined anywhere in the indexed pages. llms.txt lists no "API Keys" or "Authentication" reference page that would explain how to generate one, what lsk_ means, key scopes, expiration, or rotation. The MQTT auth flow (Issue 8) also expects an API key as the password but never says whether it's the same lsk_ key.
Consequence: A developer who picked "API Key" because they assumed they had one is stuck — there's no answer to where to generate it, what lsk_ stands for, or whether it can be rotated. Agents can't validate the prefix client-side or recognize keys in logs/errors. Agents helping a user onboard tend to invent a path (e.g. fake https://heylua.ai/settings/api-keys URLs).
The fix: Publish a dedicated "API Keys" reference page that documents: where in admin.heylua.ai to create one, the meaning of the lsk_ prefix, key scopes, expiration, rotation, and which subsystems (CLI, devices/MQTT, Socket.IO) accept which keys. Link it from llms.txt and from every place a key is required.
8. MQTT auth scheme is buried inside a code block, never explained in the security section (significant)
Problem: The MQTT connection lifecycle tab specifies:
1. Client connects to wss://mqtt.heylua.ai/mqtt
- username: {agentId}:{deviceName}
- password: {apiKey}
- LWT: offline status (retained)
This is the only place the docs say the API key is sent as the MQTT password and that the username is the colon-joined agentId:deviceName. The "Security Model" section on the same page talks about TLS and "API Key Authentication" generically but never explains the MQTT-specific credential mapping, never says whether {apiKey} is the same lsk_ key from the CLI flow, and never says how to rotate it without dropping device sessions.
Consequence: A developer building a device client has to reverse-engineer the auth scheme from a single bullet inside a tabbed example. Agents writing IoT integrations will likely produce code that puts the API key in a header instead of the MQTT password field, or will fail to construct the colon-joined username correctly.
The fix: Promote the MQTT credential mapping to the "Security Model" section: explicitly state username = "{agentId}:{deviceName}", password = <API key>, link to the API Keys reference (Issue 7), and document rotation behavior under live MQTT sessions.
9. Devices transport docs mix port and protocol claims (minor)
Problem: The Transport Comparison table says MQTT's default URL is wss://mqtt.heylua.ai/mqtt (i.e. MQTT-over-WebSocket). The Security Model section then states: "MQTT connects over port 443 (WebSocket) with TLS." The transport row labels the protocol as "MQTT 3.1.1 over TLS" with no mention of WebSocket framing. Three places, three subtly different framings of the same transport.
Consequence: A developer wiring up a device client has to decide between an MQTT-over-TCP-on-8883 client (what "MQTT 3.1.1 over TLS" usually implies) and an MQTT-over-WebSocket client (what wss:// and "port 443 (WebSocket)" imply). Picking the wrong client library is a several-hour debugging session. Agents writing IoT integrations will often default to TCP/8883.
The fix: Standardize on a single phrase across the page — e.g. "MQTT 3.1.1 over WebSocket Secure (WSS) on port 443" — and update the table, security model, and connection lifecycle blocks together.
10. llms.txt does not advertise llms-full.txt (minor)
Problem:llms.txt is published as the agent index and links to per-page .md files. Mintlify also exposes an llms-full.txt (the consolidated full-text variant), but llms.txt itself does not link to it, so agents that respect only the manifest will miss the consolidated file.
Consequence: Agents that fetch llms.txt and stop there will issue N round-trips for N pages instead of one fetch for the full corpus. This makes Lua's docs more expensive and slower to ingest than they need to be, even though the consolidated file already exists.
The fix: Add an llms-full.txt entry to the top of llms.txt so agents discover it. Mintlify generates this automatically; just link it.
What they do well
llms.txt is comprehensive and uses descriptive titles for every page — the manifest layer is in good shape, even if the OpenAPI link behind it isn't.
Devices section provides a clear side-by-side transport comparison (Socket.IO vs MQTT) with concrete URLs, which is rare for agent-platform device docs.
Credential storage is documented per-OS (macOS Keychain, Windows Credential Vault, Linux libsecret) — that level of specificity is unusual and helpful.
Top 3 recommendations
Replace the placeholder openapi.json with a real Lua API spec served over HTTPS, or remove the OpenAPI link from llms.txt. This is the single biggest agent-ingestion bug on the site.
Publish authoritative reference pages for the things developers and agents most need to look up but currently can't find: API base URLs, API keys (lsk_ prefix), and published packages.
Fix the localhost workaround for the chat widget so developers don't have to hardcode environment: "production" in dev — and document a real dev mode.
Fixed
0
Skip
17
Fail
2
Failing pages
https://docs.heylua.ai/chat-widget/installation
Summary
Verified runnable snippets from six public Lua AI documentation surfaces. The CLI install/auth flow on getting-started/installation works end-to-end (the supplied API key authenticated lua auth configure --api-key … successfully and the CLI subcommands documented on that page resolve as written). The chat-widget/installation page references an npm package @lua/pop that is not present in the public npm registry — both the npm and pnpm install snippets return 404 Not Found. The CDN script for the same widget (lua-pop.umd.js) is reachable and the inline-config / platform snippets are syntactically valid JavaScript. The OpenAPI document at api-reference/openapi.json parses as valid JSON (note: still contains the Mintlify "Plant Store" sample title rather than a Lua-specific spec). The overview/persona and devices/how-it-works pages contain only illustrative prose / mermaid diagrams with no executable snippets. llms.txt is an index file with no runnable code.
Skips break down as: yarn-only and nvm snippets where those runtimes are not installed, framework fragments (React/TSX/Vue/Next.js/Angular) that require a build harness or undefined helpers (generateSecureSessionId, getAuthToken), [command] placeholder fragments, and the WordPress HTML snippet that embeds a <?php …?> token requiring a WordPress runtime.
Required credentials
LUA_API_KEY — supplied by the user (used non-interactively via lua auth configure --api-key <key>); never echoed.
URL returns 200 OK, served as application/javascript, ~4.86 MB UMD bundle.
2
window.LuaPop.init();
PASS
JS syntax valid (executed under mocked window).
3
window.LuaPop.init({ agentId, position })
PASS
JS syntax valid.
4
Complete CDN HTML example
PASS
Valid HTML with reachable script src + valid init call.
5
npm install @lua/pop
FAIL
Registry returns E404 Not Found for @lua/pop. Diagnosis: package name in docs does not exist on the public npm registry. Suggested fix (not applied/verified): publish the package under @lua/pop, or update the docs to point at the actual published name (e.g. lua-pop if that is the intended scope).
6
yarn add @lua/pop
SKIP
Runtime unavailable: yarn not installed (and registry resolution would 404 anyway).
7
pnpm add @lua/pop
FAIL
ERR_PNPM_FETCH_404 GET https://registry.npmjs.org/@lua%2Fpop: Not Found. Same diagnosis as #5.
8
React LuaPopWidget TSX import
SKIP
Partial fragment: depends on missing @lua/pop package and a React build pipeline.
9
Vue 3 <LuaPopWidget> SFC
SKIP
Partial fragment: depends on missing @lua/pop and Vue build.
10
TypeScript LuaPopConfig import
SKIP
Partial fragment: depends on missing @lua/pop and TS toolchain.
11
WordPress HTML (with <?php echo get_option('luapop_agent_id'); ?>)
SKIP
Partial fragment: requires WordPress/PHP rendering pipeline; without PHP, agentId resolves to the literal PHP tag string.
Parses as valid JSON. (Heads-up, not a failure: the info.title is still the Mintlify sample "OpenAPI Plant Store" with servers[0].url = http://sandbox.mintlify.com — likely not the intended Lua-specific spec, but the file itself is syntactically valid.)
No runnable code snippets on this page; content is illustrative persona prose (Context blocks, "Good Persona" / "Bad Persona" plaintext) without language tags or commands to execute.