Welcome to mdfy.app
Status: shipped 2026-04.
What a bundle URL returns
GET mdfy.app/b/{id} returns plain markdown. The body is composed of (in order):
- Bundle metadata block — title, description, creator, last-updated timestamp, member count.
- graph_data JSON block — the synthesised analysis: nodes, edges, themes, insights, document summaries, reading order. Wrapped in a fenced code block tagged
mdfy:graphso AI tools can ignore it if they want plain prose. - Member doc stubs — each member doc as a heading + a short summary + the URL. No full content (full content requires
?full=1).
The whole response is < 50KB for a typical 5-7 member bundle.
Query parameters
?compact— strip low-density sections from every member stub. Default off.?full=1— inline every member doc's full body inside the response, in member order. Default off. Use when you want one giant context blob.?graph=0— omit the graph_data block. Use when the receiving AI just wants prose.?recall=$Q— filter member stubs to only the ones matching query $Q. Cheap on the server (we already have the recall index per bundle).
These compose: ?compact&recall=cross-AI&graph=0 returns only the prose stubs of members matching "cross-AI", compacted, with no graph block. ~5KB total.
What we explicitly don't return
- Auth-protected member docs that the requester isn't authorised for. They're silently omitted from the stub list. The bundle's member count adjusts.
- Embedded media. The renderer-ready HTML for images is in the doc URL, not the bundle digest.
Cache headers
Public bundles: Cache-Control: public, max-age=300, stale-while-revalidate=3600. Five minutes fresh, an hour stale-served.
Restricted / private bundles: Cache-Control: private, no-store. Don't cache at edge or in the browser. Auth-sensitive content shouldn't be near any cache.
What's frozen and what's not
Frozen: the response is always markdown. JSON is wrapped in fenced blocks; never as Content-Type: application/json. This is the core of "every URL is for both humans and AIs from the same address."
Not frozen: the exact set of ? params can grow. The graph_data schema can evolve (additive only — never remove a field without a major version bump). The cache headers can be tuned.
The bet underneath this format
That markdown-as-API-response is the right design for the next decade. Every AI tool today fetches URLs and reads markdown as a first-class input. If that stops being true, this format stops being right. We're betting it doesn't stop being true.