Skip to main content

Documentation endpoints for AI agents

Choose between the llms.txt index, the full corpus, a markdown page, the OpenAPI documents, the MCP endpoint and search, and handle each one's failures.

The documentation site serves every page to software over public, read-only addresses. None of them needs a sign-in.

An agent can read the page index, one page as markdown, all pages in one file, the pages that match a query, or the OpenAPI documents. It can also call a Model Context Protocol server, MCP for short, that serves the documentation pages through tools.

This page covers reading the documentation. To let an agent operate DALP, see CLI for AI agents, whose dalp --mcp server runs dalp commands against the Platform API, or Hosted MCP server, which runs inside a DALP deployment.

The addresses on this page are relative to the documentation host, https://docs.settlemint.com. A DALP deployment also serves its own copy of the pages under /docs on its hostname. To call your own deployment, keep the path and replace the host. That copy builds these absolute links from the host in its PUBLIC_URL environment variable:

  • the llms.txt and llms-full.txt indexes
  • the Source: line of each markdown page
  • the canonical Link header of each markdown response
  • the canonical and Open Graph tags of each page
  • the URLs that MCP search and list_pages return

The DALP Helm chart sets PUBLIC_URL from the hostname that serves the documentation, unless the operator overrides it. Documentation host explains how the chart picks it.

Call these addresses from a script or a server-side client. The MCP and search endpoints answer 403 when a request has Sec-Fetch-Site: cross-site or an Origin header for another origin. They send no cross-origin resource sharing headers, so a page on another origin cannot call them. The 403 body is the plain text Forbidden, not JSON-RPC.

Choose an endpoint

Your agent has
Call
Returns
Rate limit
No page in mind
A plain text index of every page. It ends with an “Agent endpoints” section that lists the other addresses.
None
A large context and wants everything
Every page as markdown in one plain text file. It is several megabytes.
None
The path of one page
GET /docs/<path>.md, or GET /docs/<path> with Accept: text/markdown, for example /docs/api-reference/webhooks/consuming.md
The page as markdown
None
The API contract
The OpenAPI document as JSON
None
An MCP client
POST /docs/api/mcp. A GET answers 405.
The tools list_pages, get_page and search
60 requests per minute
Words, not a path
GET /docs/api/search?query=<terms>, for example /docs/api/search?query=webhook
Matching pages and text fragments as JSON
300 requests per minute

A crawl can read the index, then fetch each page with .md. The links in the table open the public host.

The OpenAPI documents in the table are the contract built with this documentation. To generate a client for a running deployment, fetch that deployment's own /api/v2/spec.json, as the API reference overview describes.

Page markdown

A page answers as markdown in two ways. Append .md to the page path, or send Accept: text/markdown to the page URL. The root page /docs is the exception: its .md address is /docs/index.md. Both forms return the same markdown with the same headers.

DOCS=https://docs.settlemint.com

curl -sSD - "$DOCS/docs/api-reference/webhooks/consuming.md"
curl -sSD - -H 'Accept: text/markdown' "$DOCS/docs/api-reference/webhooks/consuming"

Both calls return 200 and a markdown body that starts with a # Title line and a Source: line with the canonical URL. The headers that matter to a client are:

Content-Type: text/markdown; charset=utf-8
Link: <https://docs.settlemint.com/docs/api-reference/webhooks/consuming>; rel="canonical"
Vary: Accept
Cache-Control: public, max-age=300, s-maxage=900, stale-while-revalidate=3600

For the Accept form, the types text/markdown, text/x-markdown and text/plain count as markdown. A q-value is the weight from 0 to 1 that a client gives a type, and 1 is the default. Markdown wins when the highest q-value among the markdown types is greater than 0 and at least the highest q-value among text/html, text/* and */*. A request with no Accept header, or with */* alone, returns the HTML page.

An HTML response for a page that has a markdown form includes Link: </docs/<path>.md>; rel="alternate"; type="text/markdown", which points an agent to that form. An unknown page has no markdown form, so its HTML response has no such header.

The two forms differ only when a page has no markdown form:

Page state
.md suffixAccept: text/markdown alone
Page at its present path
200 markdown
200 markdown
Moved, so the old path redirects
301 to the new .md path
406 JSON
Unknown
404 markdown
406 JSON
API explorer, such as /docs/api-reference/v2
200 markdown
200 markdown

The 404 body for an unknown page reads:

# Page not found

No markdown page exists at /docs/no/such/page. The index at /docs/llms.txt lists every page.

The 406 body is {"error":"Only HTML requests are supported here"}. To get the HTML answer instead, add the HTML type to the header: Accept: text/markdown, text/html;q=0.9. A moved page then answers 301 to its new HTML path, and an unknown path answers 404 HTML.

MCP endpoint

POST /docs/api/mcp is a Model Context Protocol server over streamable HTTP, which lets the server answer a request with an event stream. Requests and replies use JSON-RPC, a JSON message format for remote procedure calls. The public site serves the endpoint at https://docs.settlemint.com/docs/api/mcp. Each request stands alone: there is no session.

This section describes requests in the protocol revisions up to 2025-11-25, which have no _meta envelope. The MCP-Protocol-Version header is optional for them. A request that names an older revision the server does not support gets 400, and the error message lists the supported older revisions. Revision 2026-07-28 uses a different request format, with its own headers and a _meta envelope in params. This page does not describe it. A request in that format that breaks its rules gets 400 with a JSON-RPC error code.

A request needs three things:

  • Content-Type: application/json. Another type gets 415.
  • An Accept header that lists both application/json and text/event-stream. */* does not count, and a request without both gets 406.
  • The POST method. GET and DELETE answer 405.

tools/call works without an earlier initialize call. This request uses the DOCS variable from Page markdown and asks for a page that does not exist:

curl -sS -X POST "$DOCS/docs/api/mcp" \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call",
"params":{"name":"get_page","arguments":{"url":"/docs/no/such/page"}}}'

The reply is an event stream with one message, and the response sends Cache-Control: no-cache, no-transform:

event: message
data: {"result":{"content":[{"type":"text","text":"page not found: /docs/no/such/page"}],"isError":true},"jsonrpc":"2.0","id":1}

The server offers three tools. Send tools/list for the exact input schemas.

Tool
Arguments
Notes
list_pages
None
The page index, with a .md URL for each page.
get_page
url
The page as markdown, starting with a # Title line and a Source: line. url is a /docs/... path, or a full URL on the origin in PUBLIC_URL, with or without .md. A /docs/... path works whichever host PUBLIC_URL names.
search
query, optional locale
Up to 60 matching records as JSON text. See Search endpoint.

get_page reports a problem in the result, not in the HTTP status, which stays 200 with isError set to true. A missing page gives the text page not found: <input>. The same result comes back for a URL on another host, a moved page and an API explorer page, because get_page reads only documentation pages at their present path, and an API explorer page is not one of them. Read an API explorer page with .md.

A missing or mistyped argument gives Input validation error: ... with the same isError.

A request for a tool that does not exist returns a JSON-RPC error object with code -32602 in place of result. A body that is not valid JSON gets 400.

Search endpoint

GET /docs/api/search?query=<terms> matches page titles, descriptions, headings and body text, and returns a JSON array.

A page whose title holds every searched word comes before a page that only mentions them. Title words match whole and ignore case and accents, without stemming. The last word may be half typed, from two letters. Only the first 201 records the engine finds are reordered.

Each matched page gives one record for the page and up to eight records for text fragments on it. Every record has id, type, content and url. A page record has type set to page, the page title in content and a breadcrumbs list. A fragment record has type set to heading or text and a url that usually ends in an anchor. A heading record has the heading text in content. A text record has the text fragment in content. A text record that comes from a table row holds the header row and that row as a markdown table in content, and has a table key that names the table. This request uses the DOCS variable from Page markdown, holds the search to one section and raises the cap:

curl -sSD - "$DOCS/docs/api/search?query=retries&tag=api-reference&limit=100"

The reply sends X-Docs-Search-Limit: 100 and an X-Docs-Search-Truncated header, and its body is an array of records. This is one page record from such a reply:

{
  "id": "/docs/api-reference/webhooks/consuming",
  "type": "page",
  "content": "Consuming events",
  "breadcrumbs": ["Documentation", "API reference", "Webhooks"],
  "url": "/docs/api-reference/webhooks/consuming"
}
Parameter
Effect
query
The terms to match. A missing or empty query returns 200 and [].
tag
Holds the search to one section: overview, getting-started, platform, console, concepts, deployment, api-reference, changelog or business. overview holds the root page /docs, and business holds the privacy policy. tag takes one value. A list such as &tag=a,b, or an unknown value, returns [].
limit
The maximum number of records, not pages. The default is 60. A value that is not a whole number of 1 or more falls back to 60, and a value over 200 becomes 200.

Every 200 response reports the cap it applied in X-Docs-Search-Limit. X-Docs-Search-Truncated: true means more records exist: raise limit up to 200, then narrow the query or add tag. false does not prove the array holds every match, because a page contributes at most eight fragments. The endpoint has no offset, page or cursor parameter. Send only query, tag and limit: a search with a query and mode=vector answers 500, because the index holds no embeddings.

Three groups of error pages are not in the search index: the error index at /docs/api-reference/errors, and the two error references at /docs/api-reference/errors/error-code-reference and /docs/api-reference/errors/platform-api-error-reference with every code-range page under them. Find them through llms.txt, or open the error index and follow the link for the code's range.

The MCP search tool takes query and locale, and ignores locale. It has no tag and no limit: it returns at most 60 records, with a page whose title holds every searched word ahead of a page that only mentions them. Nothing in the reply says whether more records exist, so use the HTTP endpoint when you need to know whether the result was cut, or need more than 60 records.

MCP search records have full page URLs on the PUBLIC_URL host, such as https://docs.settlemint.com/docs/api-reference/webhooks/consuming, while list_pages returns .md URLs. get_page accepts both.

Limits and freshness

The MCP endpoint allows 60 requests per minute for each client, and search allows 300. Both budgets count HTTP requests, not tool calls. The initialize and tools/list calls each use one request, and so does a GET that gets 405. A client over its budget gets status 429, the plain text body Too many requests and a Retry-After header in seconds. The 429 body is not JSON-RPC. The Retry-After value depends on the cause:

Retry-After
Cause
Seconds until the client's window resets
The client used its own budget.
60
The server already tracks 10,000 clients and cannot add another.
1
The Redis store that all instances share is configured but unreachable. Every caller gets 429, whatever its own rate.

The server counts a client by the address that a configured trusted proxy forwards. A deployment with no trusted proxy configured counts every caller as one client, and all callers share one budget. A request whose forwarded address is missing or invalid also counts against that shared budget. The default install sets DOCS_CHAT_TRUSTED_PROXY, the setting that names the trusted proxy for these budgets and for the docs chat, to none, as Ask AI provider key in the install values reference describes. A 429 can then come from other callers' traffic: wait for Retry-After before you send again. On a deployment behind Cloudflare that sets DOCS_CHAT_TRUSTED_PROXY to envoy, the server counts each caller by the address Cloudflare adds to X-Forwarded-For when the operator has configured Envoy Gateway as Count readers separately behind Cloudflare describes. Otherwise callers that arrive through the same Cloudflare address share a budget. A deployment that has no Redis store keeps a separate budget on each server instance.

Three kinds of 200 response send Cache-Control: public, max-age=300, s-maxage=900, stale-while-revalidate=3600: markdown pages in either form, the llms.txt and llms-full.txt files at both the /docs/... path and the root path, and the two OpenAPI JSON files. Error responses, redirects and search replies send none, and MCP event-stream replies send Cache-Control: no-cache, no-transform. A browser or agent can reuse a response for 5 minutes. A shared cache holds it fresh for 15 minutes and can serve it for up to 60 more minutes while it fetches a new copy, so an answer can be 75 minutes behind the server.

The server builds llms-full.txt on the first request after it starts and reuses that text until it restarts, so the first request takes longer than the ones after it.

When a request fails

Every endpoint on this page only reads, so any request is safe to send again. Retry a 429 after its delay, and retry a 5xx or a timeout with your own backoff. Fix the request and resend for 400, 403, 405, 406 and 415. Do not retry a 404 or an isError result with the same input. A search with a query and mode=vector answers 500 every time, so remove mode instead of retrying.

Symptom
Cause and fix
.md answers 404 with “Page not found”
No page exists at that path. Look the path up in llms.txt.
Accept: text/markdown answers 406 with {"error":"Only HTML requests are supported here"}
The page has no markdown form: it is moved or unknown. Add text/html;q=0.9 to the header to see which: 301 is moved and 404 HTML is unknown.
.md answers 301
The page moved. Follow Location.
MCP answers 415
The Content-Type is not application/json.
MCP answers 406
Accept lacks application/json or text/event-stream, or is */*.
MCP answers 405
The request used GET or DELETE. Use POST.
MCP answers 400
The body is not valid JSON, is JSON that is not a JSON-RPC message, the MCP-Protocol-Version header names an unsupported revision, or a 2026-07-28 request breaks that revision's rules.
MCP or search answers 403
The request came from another site: it has an Origin header for another origin or Sec-Fetch-Site: cross-site. Call from a server-side client.
MCP result has isError: true and page not found
The page is missing, moved, on another host or an API explorer page. Check the path in llms.txt, and read a moved page or an API explorer page with .md.
MCP result has isError: true and Input validation error
An argument is missing or has the wrong type. Match the tool definition from tools/list.
MCP answers a JSON-RPC error -32602
The tool name is wrong or missing or arguments is not an object. Send tools/list.
Search answers 500
The request has a query and mode=vector, and the index holds no embeddings. Remove mode.
Search returns [] for tag=a,b, an unknown tag or an empty query
tag takes one value from the list in Search endpoint. query is required.
The error index, an error reference or a code-range page is missing from search
Those pages are not indexed. Use llms.txt, or open /docs/api-reference/errors and follow the link for the code's range.
MCP or search answers 429
Wait for Retry-After. See Limits and freshness.
A new page is missing from a cached response
A shared cache can be 75 minutes behind. See Limits and freshness.

Was this page helpful?