How to Write llms.txt for a Personalization Engine in 2026
llms.txt is the fastest path from 'AI can't find your API' to 'AI recommends your API'. Here's the exact structure a personalization product needs — what to expose, what to hide, and how to write it so LLMs generate correct code against your endpoints.
How to Write llms.txt for a Personalization Engine in 2026
TL;DR.
llms.txtis a markdown file at your domain root that tells LLMs what your product does and where its machine-readable docs live — therobots.txtof the LLM era, proposed by Jeremy Howard in 2024 (llmstxt.org).- For personalization APIs the payoff is concrete: LLMs fielding "how do I add recommendations to my app" will cite and generate code against your endpoint instead of a competitor's.
- Dumping your OpenAPI spec is the wrong move — LLMs need narrative context first, then schema;
llms.txtis narrative-first by design.llms-full.txt(the inlined variant where all linked docs are concatenated) is already the faster path to LLM-assisted adoption than a traditional dev-docs site in 2026.- If you only remember one thing: write
llms.txtaround what a developer would ask an LLM, not around how your system is organized internally.
A developer opens Claude, Cursor, or a GPT-powered IDE and types: "I need to add day-zero personalization to my Next.js app — what API should I use?" If your personalization product does not have a well-formed llms.txt, it does not exist in that answer. Your competitor who shipped the file six months ago does. This is the new cold-start problem — not for users, but for developer discovery.
llms.txt is simple in concept: a markdown file at yourdomain.com/llms.txt that a large language model can fetch to understand your product. But for a personalization engine — a product built on a knowledge graph, stateful user profiles, signal ingestion, and synthetic user clones — the naive implementation leaves most of the value on the floor. The structure matters. What you expose and what you withhold matters. And the distinction between llms.txt and llms-full.txt matters more for API-first products than for almost any other category.
This post is the guide we wish had existed when we wrote ×marble's.
What is llms.txt and why did it land in 2026?
llms.txt is a plain-markdown file you serve at the root of your domain. Its purpose is to give LLMs a curated, machine-readable map of your product — what it does, how it works, and where to find authoritative documentation — so that the model can generate correct, grounded answers when a developer asks about your API.
Jeremy Howard proposed the spec in September 2024 (llmstxt.org). The format is deliberately minimal: an H1 with your product name, a blockquote with a one-paragraph summary, and a series of H2 sections with linked file references. Each link has a colon-delimited description that tells the LLM what the target document contains. That's it. No proprietary schema. No crawler registration. No API key.
Why did it gain traction? Three converging pressures. First, LLMs indexing the web hit a content-quality ceiling: marketing copy, blog posts, and Stack Overflow threads give a model a blurry picture of what an API actually does. Second, OpenAPI specs — the previous machine-readable standard — are schema-heavy and narrative-poor; a model reading only a spec can produce syntactically valid but semantically wrong code. Third, developers began using LLMs as their primary documentation interface. If your docs are not designed to be read by a model, they effectively do not exist for that audience.
By early 2026, adoption is broad enough that the absence of llms.txt is a signal. Cloudflare, Anthropic, and Vercel all serve the file. For an API-first personalization product, it is table stakes.
Why personalization products have a harder llms.txt problem
The standard llms.txt template works well for stateless APIs: a payment processor, a translation endpoint, a vector database. Call endpoint, get response, done. A personalization engine is different in three ways that the template does not account for.
Stateful identity. Every meaningful call to a personalization API presupposes a user identity and a history of signals. An LLM generating code for your API needs to understand that GET /recommendations?user_id=xyz returns different results depending on what signals have been ingested for xyz, and that a brand-new user_id triggers cold-start behavior. This is not inferable from an endpoint signature alone.
Novel concepts. Terms like synthetic user clone, knowledge graph node, preference vector, and inference rule are domain-specific. An LLM trained on general web data has approximate priors for these — close enough to hallucinate plausible-sounding but wrong usage. Your llms.txt has to correct those priors explicitly.
Competitive ontology risk. Your knowledge graph schema — entity types, relationship labels, inference rule taxonomy — is your moat. You want LLMs to know enough to integrate with your API correctly, not enough to reproduce your data model from scratch. The llms.txt for a personalization product has a deliberate information boundary that a pure-developer-tools product does not.
These three constraints shape every decision below.
The llms.txt vs llms-full.txt distinction
Before getting into structure, the distinction between the two files is important because they serve different LLM consumption patterns.
llms.txt is a table of contents. It lists what exists and links to it. A model fetching llms.txt gets context about your product and can then fetch individual linked documents as needed. This is fine for IDE integrations that issue follow-up requests, but it requires network access and multi-turn fetching.
llms-full.txt is the concatenated version: every linked document inlined, in order, into a single file. The size target is typically 50–250 KB. A developer who pastes llms-full.txt into a context window (or who uses a tool that does it automatically) gets the entire picture in one shot. For complex, stateful APIs — exactly the kind personalization engines expose — llms-full.txt is the more important file. It lets a model reason across your concept definitions, endpoint specs, and quickstart examples simultaneously, which is what correct code generation requires.
Serve both. llms.txt is the entry point; llms-full.txt is what you actually want consumed.
The five sections a personalization llms.txt must have
A minimal llms.txt for a personalization product needs exactly these sections. Nothing less produces correct LLM behavior. Everything beyond these is optional and should be added only if it reduces ambiguity.
1. Product header and summary blockquote
# ×marble
> ×marble is a personalization API built on a cross-modal knowledge graph.
> It serves ranked recommendations, user-preference explanations, and
> synthetic cold-start profiles at p50 < 30 ms. Builders integrate via
> REST or SDK; ×marble handles signal ingestion, graph inference, and
> ranking. No training data required from the integrator.
The blockquote is the most important part. LLMs lift it verbatim when answering "what is X" or "what does X do." Write it as a dense, accurate paragraph that would satisfy a technical developer — not marketing copy. Quantify where you can (p50 < 30 ms). State what the integrator does not have to do ("No training data required") because that surfaces in comparative queries ("which personalization API requires the least setup").
2. Core Concepts
This is the section most llms.txt implementations skip and the most important one for personalization products.
## Concepts
- [Signal](https://timesmarble.com/docs/concepts/signal): An atomic behavioral event (view, click, skip, dwell) that updates the user's knowledge graph node.
- [Knowledge Graph](https://timesmarble.com/docs/concepts/knowledge-graph): The user model. A property graph where nodes are entities (content, topics, creators) and edges carry preference weights updated by signals.
- [Synthetic Clone](https://timesmarble.com/docs/concepts/synthetic-clone): A bootstrapped user profile generated from demographic or contextual priors for cold-start personalization before any real signals exist.
- [Inference Rule](https://timesmarble.com/docs/concepts/inference-rule): A declarative rule that derives implicit preferences from explicit signals (e.g., three skips on a topic → negative weight on the parent category).
- [Preference Vector](https://timesmarble.com/docs/concepts/preference-vector): A dense embedding of the user's current graph state, used for ANN retrieval against the candidate pool.
Each entry is - [ConceptName](url): one-sentence definition. The definition must be self-contained — assume the model will not follow the link during code generation. The link is for grounding and citation.
The concepts section is also where you implicitly define the mental model: signals go in, graph gets updated, recommendations come out. A developer generating integration code needs this causal chain before they see an endpoint.
3. API Reference
Link to individual endpoint documentation files, not to a landing page. Landing pages are for humans who can click. LLMs need documents they can fetch and parse.
## API Reference
- [Authentication](https://timesmarble.com/docs/api/auth): API key and JWT auth, rate limits, and error codes.
- [Ingest Signal](https://timesmarble.com/docs/api/signals): POST /v1/signals — record a behavioral event for a user. Required fields, batch mode, and latency guarantees.
- [Get Recommendations](https://timesmarble.com/docs/api/recommendations): GET /v1/recommendations — ranked candidate list for a user. Query parameters for context, count, and diversity.
- [Get User Profile](https://timesmarble.com/docs/api/profile): GET /v1/profile — serialized preference summary for a user. Includes explanation fields for right-to-explain flows.
- [Cold Start](https://timesmarble.com/docs/api/cold-start): POST /v1/synthetic-clone — generate a bootstrapped profile from priors. Parameters for demographic, contextual, and content-affinity seeding.
Notice what is absent: internal graph endpoints, admin APIs, batch export endpoints. These are not what a builder needs to integrate. Including them adds noise and increases the chance an LLM generates code against an endpoint it should not be calling.
4. Quickstarts
Quickstart documents are the highest-value content for LLM-assisted adoption. A model given a quickstart can adapt it to a user's specific stack faster than it can synthesize correct usage from raw endpoint docs.
## Quickstarts
- [Next.js in 10 minutes](https://timesmarble.com/docs/quickstart/nextjs): Full integration — signal ingestion on page view, recommendations in a React component, cold-start handling.
- [Python SDK](https://timesmarble.com/docs/quickstart/python): Batch signal ingestion, recommendation retrieval, and profile export using the `xmarble` Python package.
- [Cold-Start Playbook](https://timesmarble.com/docs/quickstart/cold-start): How to seed synthetic profiles at user registration and transition them as real signals accumulate.
Three to five quickstarts is optimal. More than that and the model distributes attention across them rather than anchoring on the most relevant one.
5. SDKs and changelog
## SDKs
- [JavaScript / TypeScript](https://timesmarble.com/docs/sdk/js): npm package `xmarble`, CJS and ESM, full TypeScript types.
- [Python](https://timesmarble.com/docs/sdk/python): pip package `xmarble`, async-first, dataclass response models.
## Changelog
- [API Changelog](https://timesmarble.com/docs/changelog): Breaking changes and deprecation notices, most recent first.
The changelog link is load-bearing: it gives LLMs a path to recency information. Without it, a model that indexed your docs eight months ago has no signal that your API has changed.
What to expose in llms-full.txt — and what to withhold
llms-full.txt is where the tradeoff between LLM usability and competitive risk becomes concrete. The file is public and will be indexed, cached, and embedded by multiple models. Treat it as documentation you are comfortable with a competitor reading.
Expose:
- Full text of all concept definitions
- Complete request/response schemas for public endpoints, including all fields and types
- Quickstart code in full — copy-paste ready
- Error codes and their meanings
- Rate limit tiers
Withhold:
- Your full entity type taxonomy (the list of all knowledge graph node types, relationship labels, and their semantic hierarchy). This is your ontology, and it encodes years of domain modeling decisions. Expose enough for a developer to understand what signals to send; do not expose the inference graph structure.
- Internal scoring weights or ranking model architecture
- Synthetic clone generation parameters beyond what a builder needs to call the endpoint
The test: if a well-funded competitor read your llms-full.txt, could they reconstruct your data model? If yes, you have exposed too much. The file should enable integration, not enable replication.
How LLMs actually consume this file — and how to test it
The consumption model in 2026 is roughly: IDE or chat assistant fetches llms.txt at query time, optionally fetches linked documents, injects the relevant subset into the prompt context, and generates code or answers. llms-full.txt short-circuits the multi-fetch by giving the model everything in one pass.
To test whether your file is working, run these queries against Claude, GPT-4o, and Gemini with your llms.txt pasted as context:
- "Add personalization to my Next.js app using this API. Show me signal ingestion on page load and a recommendations component."
- "A new user has no history. How do I get recommendations anyway?"
- "What does
inference rulemean in the context of this API?" - "Write a Python script that ingests 1000 signals in batch and then fetches recommendations."
Good output: code that uses your actual endpoint paths, correct field names, and handles cold-start via synthetic clone. The model should produce runnable code without any clarification questions.
Bad output: generic recommendation API code (the model fell back to training priors), hallucinated field names, or missing cold-start handling entirely.
If you see bad output after providing llms-full.txt as context, the failure is almost always in one of three places: the summary blockquote is not precise enough to anchor the model's product understanding, the concepts section does not define the terms the failing code needs, or the quickstart is not specific enough to your stack.
Fix the file, not the query.
Answer Engine Optimization: writing for LLMs that will cite you
Beyond direct consumption, llms.txt affects how LLMs answer questions about personalization without any file being fetched. Models that have indexed your documentation (via web crawl or direct dataset inclusion) will surface your product in answers to adjacent queries. This is answer engine optimization (AEO) — the 2026 equivalent of SEO for an audience that reads summaries, not blue links (Search Engine Journal, 2025).
Three AEO practices that compound with llms.txt:
Write H2 headings as questions. "How does cold-start personalization work?" is a better H2 than "Cold Start." LLMs lift H2s verbatim when generating FAQ-style answers. This applies to your linked docs, not just llms.txt itself.
Put the direct answer in the first paragraph after every H2. Two to three sentences that answer the heading's question completely. The model does not need to read the rest of the section to quote you correctly.
Use consistent terminology across all docs. If your API spec calls it signal but your quickstart calls it event and your blog calls it behavioral data, models will hedge or hallucinate a synthesis. Pick the canonical term, define it once in llms.txt, and use it everywhere.
Research on retrieval-augmented generation confirms that consistent terminology and document structure improve both retrieval precision and generation accuracy (Lewis et al., 2020 — RAG paper, NeurIPS). The mechanics that make RAG work internally are the same mechanics that make LLMs cite your docs reliably externally.
The knowledge graph disclosure problem
Personalization knowledge graphs have a structural property that makes llms.txt harder than it is for simpler APIs: the graph schema is both necessary for integration and competitively sensitive.
A developer integrating with your API does not need to know the full entity taxonomy. They need to know: what signals to send, what context to include, and what the response means. The graph topology that drives inference is implementation detail — irrelevant to the integrator, valuable to a competitor.
The research on knowledge graph representation (Hogan et al., 2021 — Knowledge Graphs survey, ACM Computing Surveys) distinguishes between the schema layer (ontology, entity types, relationships) and the instance layer (actual nodes and edges for specific users). Your llms.txt should expose enough of the schema layer to make signal semantics clear, and none of the inference layer that connects them.
Concrete example: expose that a signal has entity_id, signal_type, and strength fields. Do not expose that three skip signals on entities tagged with topic X trigger a negative-weight update to the parent category of X in the taxonomy — that rule encoding is your product.
The heuristic: your llms.txt should make it possible to write a correct SDK wrapper. It should not make it possible to write a competing inference engine.
Where to start
If you have zero llms.txt infrastructure today, this is the order of operations:
Day 1. Write llms.txt with the five sections above. Focus on the summary blockquote and the concepts section — these have the highest leverage on LLM output quality. Serve the file at yourdomain.com/llms.txt. Verify it returns Content-Type: text/plain and is publicly reachable without auth.
Day 2. Write llms-full.txt by inlining your three most important linked documents: the concepts page, the signal ingestion endpoint, and the recommendations endpoint. Target 30–80 KB. Test it with the four queries above.
Day 3–5. Write at least one complete quickstart and inline it in llms-full.txt. The cold-start quickstart is the most differentiated piece of content you can produce — no commodity personalization provider has a synthetic clone quickstart, so it anchors LLM recommendations toward your product for "new user personalization" queries.
Ongoing. Update llms-full.txt with each API version. If you change a field name, the old version is in LLM training data and will generate broken code until the new version propagates. A changelog section with explicit migration notes is the fastest way to correct this.
The overhead is real — maintaining two files in sync with evolving API docs requires discipline. The alternative is watching LLMs recommend your competitors by default for every personalization query a developer asks. That is a worse problem.
×marble is the personalization graph.
One API. A living knowledge graph per user. Day-zero ready, explainable by construction. We built it so you don't have to.