Reference
AI context (llms.txt)
Feed these docs to ChatGPT, Claude, or any LLM using the machine-readable llms.txt endpoints.
These docs are designed to be ingested by large language models. Two endpoints expose the whole site in the llmstxt.org format, so you can point an AI assistant at ThirdFactor Obsidian and get accurate, source-grounded answers instead of guesses.
Why this matters
Generic model knowledge about "a KYC API" is a poor substitute for this
product's actual contract — its exact field names, its stable-decision vs.
raw-status split, its flat KYC-session webhook shape vs. the nested
application envelope. An assistant that hasn't seen these docs will
confidently invent plausible-looking endpoints and fields that don't exist.
Grounding it in llms-full.txt turns "plausible" into "correct."
The two endpoints
/llms.txt
A structured index — the site name, description, and a linked list of every documentation page with its one-line summary. Small; ideal for giving a model a map of what exists.
/llms-full.txt
The full text of every page concatenated into one document. Larger; ideal for pasting into a model's context so it can answer detailed questions without fetching each page.
Both live at the site root:
https://<docs-domain>/llms.txt
https://<docs-domain>/llms-full.txtEvery documentation page is authored in MDX with a title and description in
its frontmatter, which is exactly what the index endpoint emits — so the two
stay in sync automatically as pages are added.
Using them with an assistant
For a focused question, fetch /llms-full.txt, paste it into the conversation,
and ask. A prompt that works well:
Here are the ThirdFactor Obsidian developer docs. Using only this material,
show me how to create a KYC session and verify the webhook signature. Cite the
relevant section.
<paste contents of /llms-full.txt>Grounding the model in the pasted docs keeps it from inventing endpoints or field names.
Worked example
Here's a full round trip: fetching the corpus, asking a realistic integration question, and what a well-grounded answer looks like.
Fetch the corpus
curl -s https://kyc.acme.com/llms-full.txt -o obsidian-docs.txt
wc -l obsidian-docs.txt # sanity-check it actually downloaded contentPaste it with a specific question
Vague prompts ("how does this API work?") produce vague, padded answers. Ask for the exact thing you need to build:
Here are the ThirdFactor Obsidian docs (below). I'm building a Node/Express
backend that:
1. Creates a KYC session for a signed-up user and returns the hosted URL to
our mobile app.
2. Receives the webhook when the session settles and updates our `users`
table.
Show me the two route handlers, including idempotency handling on create and
signature verification on the webhook. Use only endpoints and fields that
appear in the docs below — if something isn't covered, say so instead of
guessing.
<paste contents of /llms-full.txt>Expect a grounded answer
A well-grounded response should look like this — specific field names, the correct header casing, and the flat KYC-session payload shape (not the nested application envelope, which is a common hallucination when a model isn't grounded):
// POST /kyc/start
app.post("/kyc/start", async (req, res) => {
const userId = req.user.id;
const r = await fetch(`${process.env.TF_BASE_URL}/v3/session/`, {
method: "POST",
headers: {
"x-api-key": process.env.TF_API_KEY!,
"content-type": "application/json",
"idempotency-key": `kyc-${userId}`,
},
body: JSON.stringify({
vendor_data: userId,
callback_url: "https://app.acme.com/kyc/done",
}),
});
const session = await r.json();
res.json({ url: session.url });
});
// POST /webhooks/obsidian (raw body middleware, not express.json())
app.post("/webhooks/obsidian", express.raw({ type: "application/json" }), (req, res) => {
const sig = req.header("X-Webhook-Signature") ?? "";
if (!verifySignature(process.env.TF_WEBHOOK_SECRET!, sig, req.body)) {
return res.status(400).send("bad signature");
}
const event = JSON.parse(req.body.toString("utf8"));
res.status(200).end();
// event.decision, event.vendor_data — flat, no nested `session` object
void queue.enqueue(event);
});Verify against the checklist
Cross-check the generated code against the go-live checklist — it's the authoritative list of what "correct" means for a production integration, independent of what any model produced.
Tip
If a model's answer cites a field or endpoint you don't recognize, search for
it in the pasted llms-full.txt yourself (Ctrl/Cmd+F). If it's not there, the
model likely drifted from the grounding text — regenerate with a stricter
instruction ("only use endpoints that literally appear in the text below").
Guardrails for AI-generated integration code
An assistant grounded in these docs is a fast way to scaffold an integration, but the safety-critical rules still apply — check any generated code against them:
Warning
The client result is advisory. Generated code must gate access on a signed
webhook or a server-side GET /v3/session/<id>/decision/, never on the
client's return value.
- API keys belong on the server only — never in browser or mobile code.
- Webhook signatures must be verified over the raw request body with a constant-time compare, rejecting stale timestamps.
- KYC session webhook payloads are flat (
event.vendor_data,event.decision), not nested underevent.session. - Branch on stable
decisionvalues, not raw statuses.
If an assistant produces code that violates any of these, treat it as a bug — the go-live checklist is the authoritative list.