Developers ask support questions with exact strings in them: an error code, an endpoint path, a header name, a line from a stack trace. An AI agent grounded in your docs answers those well when the docs put each exact string next to its explanation, and badly when the answer is spread across a long reference page. Most of the work of AI support for an API company is documentation structure, not prompt tuning.
This post explains how LayBuild's retrieval treats technical docs, how to structure them so the right section comes back, what to pin, and what LayBuild does not do for developer support today.
How your docs become retrievable chunks
When you add a document, LayBuild splits it hierarchically. It splits on markdown headers first, then paragraphs, then sentences, into parent chunks of about 350 tokens with 50 tokens of overlap, and child chunks of about 150 tokens with 25 tokens of overlap. Tokens are estimated as characters divided by 4, so a parent chunk is roughly 1,400 characters and a child roughly 600.
At question time, several retrieval legs run in parallel and their results are merged with Reciprocal Rank Fusion (k=60):
question
|
+--> Qdrant vector search (bge-small-en-v1.5 embeddings)
+--> Postgres full-text search (english config)
+--> full-text search over Q&A pairs
+--> knowledge-graph leg
|
Reciprocal Rank Fusion --> results under 0.4 relevance dropped
|
pinned documents + retrieved chunks --> LLM --> grounding check --> answerThe two legs that matter most for developer questions pull in different directions. Vector search matches meaning, so "my requests keep getting throttled" can find a section titled "Rate limits". Full-text search matches words, so a pasted error name finds the section that contains that exact name. You want both to succeed, which gives you the structural rules below.
Give every error code its own heading
Put each error code or error message under its own ## or ### heading, with the exact string in the heading. Using a hypothetical API as the example, a section might look like this:
## 429 rate_limit_exceeded
You sent more requests than your plan allows in the current window. The response includes a `Retry-After` header with the number of seconds to wait. Retry with exponential backoff and respect `Retry-After`, as in the example below.Followed directly by a short example the agent can quote:
import time
import requests
def post_with_retry(url, payload, headers, attempts=5):
for attempt in range(attempts):
r = requests.post(url, json=payload, headers=headers, timeout=10)
if r.status_code != 429:
return r
# The server's Retry-After wins; backoff is only the fallback when it is missing.
time.sleep(int(r.headers.get("Retry-After", 2 ** attempt)))
return rBecause the chunker splits on headers first, each error becomes its own chunk with its explanation attached. The heading gives full-text search the exact token, and the prose gives vector search the meaning. A single table listing fifty error codes with one-line descriptions does the opposite: it becomes a few large chunks where every code competes with every other.
Write the cause and the fix in the same section. If the fix lives on a different page, a customer's question may retrieve the error description without the fix, and the agent can only answer with what it retrieved.
Keep code blocks short and next to their explanation
A code block longer than about 1,400 characters will not fit in one parent chunk, and the splitter falls back to paragraph and sentence boundaries, which can separate the setup lines from the call. Keep examples to the minimum that runs, put a sentence before each one saying what it does, and split long examples into steps under their own headings.
Show the same call in each language your customers use, each under its own heading ("Create a payment in Python", "Create a payment in Node.js"). This also matters for a limit covered below: the agent refuses requests phrased as "write me a script", and a grounded answer can only quote code that exists in your docs.
Import docs in the shapes LayBuild accepts
LayBuild accepts file uploads (markdown, text, PDF, Word, PowerPoint, spreadsheets, CSV, EPUB and a few others, 10 MB each by default) and URLs. For URLs, it fetches only the pages you list, converts the HTML to markdown and keeps up to 50,000 characters per page. It does not crawl links or read sitemaps.
For an API reference, that means two practical choices. Either list each reference page URL individually, or export your docs as markdown and upload the files. If a single reference page is longer than 50,000 characters, the rest is cut off, so split it or upload the markdown.
Plans cap the number of documents and URLs (for example, Starter allows 10 documents and 5 URLs; see pricing), so large doc sites need to be consolidated into fewer, well-structured files.
Pin the changelog and status, and keep them short
Pinned documents are added to every prompt the agent sends to the LLM, alongside whatever retrieval returns. By default, each pinned document is capped at 4,000 characters and all pinned content at 12,000 characters, and anything past the cap is cut from the end. Use pinning for content that answers many different questions:
- A short changelog with the newest entries first, so the cap cuts the oldest ones.
- Your versioning and deprecation policy.
- Known incidents or current limitations, with dates.
Pinned content is only as current as the last time you updated it. LayBuild does not re-sync URLs on a schedule, so a pinned status page fetched last month says what it said last month. When you ship a release or open an incident, update the pinned document as part of that process.
Use API tools for fixed calls, not account lookups
LayBuild's API tools are HTTP calls you attach to an agent, with auth (none, bearer, basic or API key), headers and parameters you configure. They are not LLM function calling. After the agent writes its answer, each attached tool is called with its configured values, and the first successful result is shown alongside the reply. The model does not choose the tool or fill in arguments from the conversation.
That makes them suitable for fixed, public, read-only data, such as a GET to your public status endpoint returning current component health. It runs on every answered turn of that agent, so attach it only to an agent where showing that data every time makes sense.
It does not make them suitable for account lookups. A tool call does not carry the customer's identity or any value from the conversation, so "why was my API key revoked?" or "what is my current usage?" cannot be answered by querying your backend for that customer. Those questions need a human with access to your admin tools, or your own dashboard.
Know what the agent will refuse
Two behaviours surprise developer-tool teams.
LayBuild refuses some general-knowledge requests before the LLM sees them, and those patterns include requests to write code, such as "write me a Python script" or "write a SQL query". A customer asking the agent to write integration code gets the fixed refusal. Asking "how do I create a payment in Python?" goes through retrieval normally, which is why your docs need the samples.
The agent answers only from your content. If retrieval finds nothing, or fewer than 25% of the answer's stemmed words appear in the retrieved sources, the answer is replaced with a fixed reply: "I do not have specific information about that in the knowledge base...". That check is lexical. It can block a correct answer that paraphrases heavily, and it can let through a wrong one that reuses source vocabulary. Code copied from your docs overlaps well; answers that reason beyond the docs get replaced.
What LayBuild does not do for developer support today
Be clear on these before you plan a rollout. There is no GitHub, GitBook, Notion, Confluence or Google Docs sync, and no scheduled re-import, so docs changes reach the agent only when someone re-uploads or re-imports them. There is no OpenAPI import for tools. There is no stack-trace parsing: a pasted trace is searched as text, so the error line itself needs to appear in your docs. The default embedding model and full-text search are English, so questions in other languages retrieve less well. Channels are the web widget, WhatsApp and the REST API; there is no Slack or Discord integration for community support.
For handing technical escalations to engineering, subscribe to the CONVERSATION_HANDOFF webhook and open an issue in your own tracker from your endpoint.
Next steps
- Restructure your ten most common error codes under their own headings, re-upload, and test with the exact strings customers paste.
- Read optimizing chunk size for RAG retrieval and hybrid search with Qdrant for more on the retrieval side.
- Browse the API and webhook reference on the developers page.
