Schema overview
This documentation site is powered by GraphQL. Every page you are reading lives behind a live, read-only GraphQL API: the documentation is the data. Each Markdown file under docs/ is loaded into a Doc record that you can fetch, filter, and full-text search — programmatically, from a script or an AI agent.
The endpoint is a single public surface: read-only, no keys, no auth, no mutations. It runs on GraphQL Yoga with @escape.tech/graphql-armor guardrails.
The endpoint leaves introspection on and ships GraphiQL. An agent can fetch the full SDL at runtime instead of being handed it — point a client at the endpoint and discover the schema live. The types below are the contract either way.
Reading the snippets
The blocks below are the real Schema Definition Language (SDL), reproduced from the server. A trailing ! means non-null; [Type!]! is a non-null list of non-null items. Fields without ! (like description and category) may come back null.
The object types
Doc — a documentation page
Doc is the center of the schema. One Doc represents one rendered page on this site, with its frontmatter, heading outline, and full Markdown body all queryable.
type Doc {
"Stable id — the path under docs/ without extension, e.g. \"graphql/queries\"."
id: ID!
"Source file path relative to docs/."
path: String!
"Site URL path for the rendered page, e.g. /docs/graphql/queries."
slug: String!
title: String!
description: String
"Top-level section, e.g. graphql, chat, organization, tooling."
section: String!
"Sub-category when nested, otherwise null."
category: String
"The page's heading outline."
headings: [Heading!]!
"The Markdown body with frontmatter stripped."
body: String!
wordCount: Int!
}
idis the stable key — the path underdocs/without its extension (e.g.graphql/queries). Use it with thedoc(id:)query.slugis the live site URL path (e.g./docs/graphql/queries), so a search result links straight back to the rendered page.sectionis the top-level folder (graphql,chat,organization, …);categoryis the nested sub-folder when one exists, otherwisenull.headingsis the page outline;bodyis the raw Markdown with frontmatter stripped;wordCountis the body length, handy for ranking or budgeting context.
Heading — one entry in a page's outline
type Heading {
"Heading level, 1-4 (h1-h4)."
depth: Int!
text: String!
"Slugified anchor for deep-linking."
anchor: String!
}
Each Heading carries its depth, the visible text, and a slugified anchor you can append to a Doc.slug to deep-link into a specific section.
Section — a group of docs
type Section {
name: String!
docCount: Int!
docs: [Doc!]!
}
A Section rolls up every Doc that shares a top-level section. docCount is the length of docs, so it always matches what you would get back.
ApiMeta — describe-thyself metadata
type ApiMeta {
name: String!
description: String!
docCount: Int!
sections: [String!]!
endpoint: String!
}
ApiMeta is a one-shot orientation record: the API's name and description, the total docCount, the list of sections, and the endpoint path. An agent can open with a single meta query to learn the shape of the corpus before drilling in.
The Query root
There are five entry points — all reads. There is no Mutation type: this endpoint is read-only by design.
type Query {
"All docs, optionally filtered by section and/or a free-text search term."
docs(section: String, search: String, limit: Int = 100): [Doc!]!
"A single doc by id (e.g. \"graphql/queries\") or by slug. Null if no match."
doc(id: ID, slug: String): Doc
"Sections with their docs and counts."
sections: [Section!]!
"Full-text search across title, description, and body."
search(query: String!, limit: Int = 20): [Doc!]!
"API metadata — name, doc count, sections, endpoint."
meta: ApiMeta!
}
| Field | Returns | Purpose |
|---|---|---|
docs(section, search, limit) | [Doc!]! | List docs, optionally filtered by section and/or a free-text search term. limit defaults to 100. |
doc(id, slug) | Doc | Fetch a single page by id or slug. Returns null if nothing matches. |
sections | [Section!]! | Every section with its nested docs and counts. |
search(query, limit) | [Doc!]! | Full-text search across title, description, and body. limit defaults to 20. |
meta | ApiMeta! | API self-description — name, doc count, section list, endpoint. |
See Queries for full request/response walkthroughs of each field.
See the corpus
A single meta query orients you to the whole dataset:
query {
meta {
name
docCount
sections
endpoint
}
}
Run it against the live endpoint:
curl -sS http://localhost:4000/graphql \
-H 'content-type: application/json' \
--data '{"query":"query { meta { name docCount sections endpoint } }"}'
{
"data": {
"meta": {
"name": "Keen Docs GraphQL API",
"docCount": 49,
"sections": ["chat", "graphql", "keen-extension", "onboarding", "organization", "root", "tooling"],
"endpoint": "/graphql"
}
}
}
The corpus is 49 docs across seven sections: chat (7), graphql (6), keen-extension (15), onboarding (10), organization (9), root (1), and tooling (1).
Searching returns matching pages with links back to the live site — the response shape mirrors your selection set exactly:
curl -sS http://localhost:4000/graphql \
-H 'content-type: application/json' \
--data '{"query":"query { search(query: \"introspection\", limit: 5) { id title slug section } }"}'
{
"data": {
"search": [
{ "id": "graphql/agents", "title": "Drive the docs from an agent", "slug": "/docs/graphql/agents", "section": "graphql" },
{ "id": "graphql/errors-and-limits", "title": "Errors and limits", "slug": "/docs/graphql/errors-and-limits", "section": "graphql" },
{ "id": "graphql/index", "title": "GraphQL API", "slug": "/docs/graphql", "section": "graphql" },
{ "id": "graphql/queries", "title": "Queries", "slug": "/docs/graphql/queries", "section": "graphql" },
{ "id": "graphql/running-the-api", "title": "Running the API", "slug": "/docs/graphql/running-the-api", "section": "graphql" }
]
}
}
Fetch the SDL live
Because introspection is enabled, you never have to take this page on faith — ask the server for its own types. Any GraphQL client, or a raw introspection query, returns the same SDL:
curl -sS http://localhost:4000/graphql \
-H 'content-type: application/json' \
--data '{"query":"query { __type(name: \"Doc\") { fields { name type { kind name ofType { kind name } } } } }"}'
Or open GraphiQL in a browser at http://localhost:4000/graphql and use the built-in Docs and Schema explorers to browse Doc, Heading, Section, ApiMeta, and the Query root interactively.
Where to go next
- GraphQL API — the endpoint, the stack, and your first query.
- Queries — every root field with variables and sample responses.
- Drive the docs from an agent — how an AI agent reads this corpus end to end.
- Errors and limits — the graphql-armor guardrails (max depth, aliases, tokens, cost) that bound every request.
The connection graph
Pages link to each other, and the API exposes those links — so you can treat the whole docs site as one connected graph.
Doc.links: [ID!]!— the ids of the docs a page links to (its outgoing edges).graph: DocGraph!— the entire site at once: every page as a node, every cross-link as an edge.
type GraphNode { id: ID! title: String! section: String! slug: String! }
type GraphEdge { from: ID! to: ID! }
type DocGraph { nodes: [GraphNode!]! edges: [GraphEdge!]! }
curl -sS http://localhost:4000/graphql -H 'content-type: application/json' \
--data '{"query":"{ graph { nodes { id section } edges { from to } } }"}'
A ready-made visualization of this graph is served at http://localhost:4000/graph — see Running the API.