The Discovery API is a public, unauthenticated endpoint that serves the platform's own integration documentation as machine-readable Markdown. It's designed to be the first thing an AI agent or MCP server reads when figuring out how to call AppEngine — fresher than any cached doc, and version-controlled with the code.
What it is
A handful of Markdown files in /Users/imzee/projects/appengine/docs/discovery/ (00-index.md, 01-auth.md, 02-data.md, 03-sites.md, 04-storefront.md, 05-crm.md, 09-sales-channels.md, 11-client.md) served verbatim by DiscoveryController. Each file documents one domain — what it does, the headers it expects, the endpoints it exposes, and worked curl examples. The files are hand-maintained alongside the controllers they describe.
Three things make this useful for AI integrations:
- Always fresh — edits go live the moment they're deployed; there is no doc-publishing pipeline drift.
- Public — no auth required to read. An agent can fetch it before it has any credentials.
- Stable index —
00-index.mdis canonical. Start there, follow links, find the section you need.
Endpoints
/discoverNo auth/discover/listNo auth/discover/{name}No authThe controller is mounted at /discover (note: not /discovery — confirm the prefix in your deployment). All routes are decorated @PublicRoute() and respond with Content-Type: text/markdown; charset=utf-8.
The index file
GET /discover returns 00-index.md — the entry point. From the source:
Appmint is a multi-tenant SaaS platform that bundles a CMS, commerce, CRM, banking, HR, AI, and integration layer behind one API. Every organization (tenant) is isolated by an
orgId. Inside an org you have Users (staff who run the org) and Customers (end-buyers of the org's services).
The index covers authentication headers, principal types (User vs Customer), the BaseModel\<T\> envelope, the universal CRUD endpoint, naming conventions, and a module map that links to every other discovery doc.
Listing available docs
GET /discover/list returns a JSON envelope (the only non-Markdown response from this controller):
{
"baseUrl": "https://appengine.appmint.io/discover",
"files": [
{ "file": "00-index.md", "url": "https://appengine.appmint.io/discover/00-index" },
{ "file": "01-auth.md", "url": "https://appengine.appmint.io/discover/01-auth" },
{ "file": "02-data.md", "url": "https://appengine.appmint.io/discover/02-data" },
{ "file": "03-sites.md", "url": "https://appengine.appmint.io/discover/03-sites" },
{ "file": "04-storefront.md", "url": "https://appengine.appmint.io/discover/04-storefront" },
{ "file": "05-crm.md", "url": "https://appengine.appmint.io/discover/05-crm" },
{ "file": "09-sales-channels.md", "url": "https://appengine.appmint.io/discover/09-sales-channels" },
{ "file": "11-client.md", "url": "https://appengine.appmint.io/discover/11-client" }
]
}
An agent can fetch this once, then crawl each url to build its full picture of the API surface.
Fetching a section
GET /discover/{name} serves the file. Either form works: /discover/02-data and /discover/02-data.md resolve to the same file. The handler validates the name against ^[a-z0-9][a-z0-9\-_]*\.md$ to prevent path traversal.
curl https://appengine.appmint.io/discover/02-data
The response is raw Markdown — feed it to your LLM unchanged.
How an AI agent should consume it
1. GET /discover -> 00-index.md
2. Parse the module map.
3. Pick the section that matches the user's intent.
4. GET /discover/{section} -> the relevant Markdown.
5. Read curl examples and endpoint blocks.
6. Construct the call.
7. If unsure, fall back to the Repository Controller (02-data.md).
The index file ends with a ## 8. How to use this doc as an AI agent section that codifies the same flow.
MCP integration shape
If you're wiring AppEngine into a Model Context Protocol server, the Discovery API is the natural source for tool definitions. A minimal MCP tool:
{
"name": "appmint_discover",
"description": "Fetch a section of the AppMint API discovery docs.",
"inputSchema": {
"type": "object",
"properties": {
"section": {
"type": "string",
"description": "Section name without extension, e.g. '02-data', '04-storefront'. Pass empty for the index."
}
}
}
}
The implementation is one HTTP GET. Since the docs are public and small (hundreds of KB total), an MCP server can also pre-fetch them at startup and serve from cache, refreshing on a schedule.
Caching
Each Markdown file is read from disk on every request. There's no server-side caching. With small files and warm filesystem buffers this is fine, but downstream proxies (CDN, reverse proxy) can safely cache responses for minutes — the docs change rarely.
Cache-Control headers are not currently set by the controller; check your edge cache rules.
Error responses
| Code | Meaning |
|---|---|
| 400 | Section name failed the safe-name regex (path-traversal attempt or weird characters). |
| 404 | The named file doesn't exist on disk. The handler logs the miss and surfaces a clean 404. |
| 500 | The docs directory is unreadable — usually a deployment bug (file not packaged) rather than a runtime issue. |
Updating the docs
The discovery files live in source control: appengine/docs/discovery/*.md. Edits are reviewed alongside controller changes and ship with the same deploy. There is no separate publishing step.
This puts a small but real burden on every developer adding an endpoint: update the matching discovery section in the same PR. The trade-off is documentation that's never out of date relative to the running code.
What's NOT here
- The discovery API does not auto-generate docs from controllers via reflection or decorators. The Markdown is hand-written.
- It is not a Swagger/OpenAPI replacement. AppEngine does ship Swagger — see
/api(or your deployment's Swagger path) — for an interactive type-level reference. Discovery and Swagger are complementary: Swagger gives you exact request/response schemas; Discovery gives you intent and worked examples. - It is not versioned. There's no
/discover/v2/. When the API breaks compatibility, the docs are updated in place.
For raw HTTP conventions — headers, error envelope, auth combinations — see Raw REST.